형변환

하나의 자료형을 다른 자료형으로 바꾸는 행위를 형변환(Type Conversion)이라하며 캐스팅(Casting)이라고 부르기도 합니다.

서로 다른 자료형 간의 연산을 수행할 때, C++ 컴파일러는 동일한 자료형으로 변환한 후에 연산을 수행해야하기 때문에 형변환을 합니다.

다음과 같은 경우에 자동으로 형 변환을 수행합니다.

  1. 다른 자료형끼리의 대입, 산술 연산 시
  2. 함수에 인수를 전달할 때

표현 범위가 작은 자료형에서 표현 범위가 더욱 큰 자료형으로의 형변환은 큰 문제가 되지 않지만, 반대의 경우 즉 표현 범위가 작은 자료형으로의 형변환은 데이터의 손실이 발생합니다.

형변환을 하는 과정에서 암시적(implicit)또는 명시적(explicit)으로 자료의 형식을 변환하는데, 잘못된 형변환은 시스템 전반에 영향을 끼치기 때문에 주의해야합니다.

묵시적 형변환(자동 형변환)

묵시적 형변환은 컴파일러가 자동으로 수행해주는 형변환을 의미합니다.

표준변환은 묵시적 형변환의 한 형태입니다.

이 형변환은 한번의 변환으로 대상 타입과 직접 변환 가능한 관계가 있을 때만 표준 변환이 적용됩니다.
즉, 여러 단계의 변환이 필요한 경우 표준 변환으로 해결할 수 없으며, 명시적 형변환이 필요합니다.

예시는 다음과 같습니다.

class A {};
class B : public A {};
class C : public B {};

A* a = new C();
B* b = static_cast<B*>(a);

CA의 하위 클래스이고, 업캐스팅이므로 C* → B* → A*와 같은 모습이 됩니다.
최종적으로 A*로 직접 변환 가능하므로 단일 단계 변환입니다.
결과적으로 표준 변환 규칙에 따라 자동으로 변환 될 수 있습니다.

B* b = a;를 하려고 할 경우, 다운캐스팅이며, 다단계 변환이 필요합니다.
다운 캐스팅을 자동으로 수행하지 않는 이유는 안전성 문제 때문에 명시적으로 형변환해야 합니다.

만약 명시적으로 하지 않는다면 다음과 같은 문제가 있을 수 있습니다.

A* a = new A();
B* b = a;

해당 예시에서 a는 실제로 B*가 아닌데, 만약 이 과정이 자동으로 이루어 진다면 잘못된 다운캐스팅이 발생할 수 있고, 실행 중에 많은 문제를 초래할 수 있습니다.

산술 변환

산술 연산이 수행될 때, 두 피연산자가 다른 자료형일 경우 이를 같은 자료형으로 변환해야 연산을 진행할 수 있습니다.

이 변환은 정수 승격(Integer Promotion)과 공통 타입 변환(Arithmetic Conversion)을 포함합니다.

정수 승격은 정수형 데이터가 변환 기준보다 작은 경우, 변환 기준에 맞게 승격됩니다.
char, short와 같은 작은 정수형 자료형은 정수 승격에 의해 int로 승격됩니다.
이 승격 규칙에 따라, 작은 정수형 자료형이 연산에 참여하면 자동으로 int로 변환됩니다.

C++의 정수 승격의 기준은 최소 int입니다.

공통 타입 변환은 두 피연산자의 자료형이 서로 다를 경우, 보다 표현 범위가 넓은 자료형으로 변환됩니다.

변환 우선순위는 다음과 같습니다.

  1. long double
  2. double
  3. float
  4. unsigned long long
  5. long long
  6. unsigned long
  7. long
  8. unsigned int
  9. int

예시는 다음과 같습니다.

대입 연산 시 연산자의 오른쪽에 존재하는 데이터의 자료형이 연산자의 왼쪽에 존재하는 데이터의 자료형으로 암시적 형변환이 진행됩니다.

int num1 = 3.1415;
double num2 = 5;

num1의 연산이 수행된다면 int형에 실수를 대입하므로 소수 부분이 자동으로 삭제되어 데이터의 손실을 발생합니다.
num2의 연산이 수행된다면 범위가 큰 double형에 상대적으로 범위가 작은 int형 데이터를 대입하므로 문제가 되지 않습니다.

double num1 = 5 + 3.14;
double num2 = 5.0f + 3.14;

num1의 연산은 int형과 double형 데이터의 산술 연산입니다.
데이터의 손실이 최소화되도록 int형 데이터가 double형으로 자동 형변환됩니다.

num2의 연산은 float형과 double형 데이터의 산술 연산입니다.
마찬가지로 데이터의 손실이 최소화되도록 float형 데이터가 double형으로 자동 형변환됩니다.

포인터 변환

포인터 간의 표준 변화는 상속 관계, void*를 중심으로 이루어집니다.

모든 포인터는 void*로 변환될 수 있습니다.
하지만 void*에서 원래의 자료형 포인터로 암시적 변환은 허용되지 않습니다.
이 경우 명시적 캐스팅이 필요합니다.

int a = 10;
void* ptr1 = &a;
int* ptr2 = (int*)p;

void* ptr1 = &a;은 암시적 형변환이 이루어집니다.
int* ptr2 = (int*)p;은 명시적 형변환이 이루어진 경우입니다.


상속 관계에서 포인터 변화는 업캐스팅과 다운캐스팅이 있습니다.

업캐스팅(Upcasting)은 파생 클래스 포인터를 기반 클래스 포인터로 변환하는 것입니다.
이 경우 암시적 변환이 가능합니다.

다운캐스팅(Downcasting)은 기반 클래스 포인터를 파생 클래스 포인터로 변환하는 것입니다.
이 경우 명시적 변환이 필요합니다.

class Base {};
class Derived : public Base {};

Derived d;
Base* basePtr = &d;
Derived* derivedPtr = (Derived*)basePtr;

Base* basePtr = &d;은 업캐스팅으로 암시적 형변환이 이루어졌습니다.
Derived* derivedPtr = (Derived*)basePtr;은 다운캐스팅으로 명시적 형변환이 이루어졌습니다.

함수 매개변수 변환

함수 호출 시 전달된 인수가 함수 매개변수와 정확히 일치하지 않을 경우 표준 변환이 이루어집니다.
이때 자료형은 함수에서 호출 될 수 있는 자료형으로 형변환해서 함수를 호출합니다.

void function(double x);

int main() {
    function(10);
    return 0;
}

int자료형인 10double로 변환되어 매개변수로 넘어갑니다.

참조 변환

const객체를 const참조로 변환이 가능합니다.

int a = 10;
const int& ref = a;

배열 변환

배열의 첫 번째 원소를 가리키는 포인터로 변환됩니다.

int arr[5] = {1, 2, 3, 4, 5};
int* p = arr;

arr이 첫 번째 원소를 기리키는 int*로 변환됩니다.

명시적 형변환(강제 형변환)

사용자가 캐스트(Cast) 연산자를 사용하여 강제적으로 수행하는 형변환을 가리킵니다.

C 스타일의 명시적 형변환은 두가지가 있습니다.

double num1 = 3.1415;
int num2 = (int)d;
int num3 = int(d);

이렇게 형변환 하는 방식을 C++에서는 C 스타일 형변환(캐스팅) 혹은 오래된 형변환이라고 하며, 아직까지 사용되고 있지만 C++에서 형변환을 위한 4가지의 연산자가 따로 제공됩니다.

static_cast

static(정적) 캐스트 연산자입니다.

연산자를 통해 형변환을 하면 컴파일(정적) 타임에 형변환이 가능한지 검사합니다.
형변환이 적합한지 검사해주고, 그 과정에서 에러를 발생 시켜줍니다.

기본 자료형간의 형변환을 허용하며, 최대한 값을 유지합니다.

상속 관계에 있는 클래스 자료형의 형변환도 허용합니다.
부모를 자식으로 형변환 하는것을 다운캐스팅이라 하는데, 이때 부모는 자식 클래스의 멤버를 가지고 있지 않기 때문에 크래시가 날 수 있어 안전하지 않습니다.

다른 형변환 연산자중 가장 사용빈도가 높습니다.

dynamic cast

컴파일 시점이 아닌 런타임시 형변환을 검사하여 형변환을 보다 안전하게 처리합니다.

포인터 또는 참조형를 대상으로만 사용이 가능합니다.
다형성을 기반으로, 부모 클래스와 자식 클래스간의 안전한 형변환에 사용됩니다.

하나 이상의 virtual함수가 존재해야하며, 호환되지 않는 자식형으로 캐스팅하려하면 nullptr를 반환하거나 bad_cast 예외를 던질 수 있습니다.

런타임 형식 정보(RTTI)라는 기능 덕분에 사용 가능한데 성능을 중요시하는 C++에서 속도를 저하시키며, 용량이 커지는 문제가 있어 끄는게 일반적이라 사용빈도가 낮습니다.

const cast

이 형변환 연산자는 다른 자료형으로 바꿀 수는 없지만 const 성질을 제거하고 싶을 때 사용합니다.

const가 가진 의미인 객체가 변하지 말아야한다는 작성자의 의도를 다른사람에게 알려주는 수단 중 하나가 사라지므로 const속성을 제거하는건 좋지 않아 특별한 상황이 아니라면 사용하지 않습니다.

reinterpret_cast

재해석(reinterpret) 캐스트 연산자는 메모리 레이아웃을 강제로 변환하는 연산자입니다.
일반적으로 허용하지 않는 형변환을 강제적(무조건)으로 형변환할 때 사용합니다.

자료형간의 포인터나 참조를 강제로 변환할 수 있지만, 이 형변환을 남용하면 프로그램이 비정상적으로 동작할 수 있습니다.

특별한 경우를 제외하고는 사용을 피하는게 좋습니다.

Date:     Updated:

카테고리:

태그:

Cpp 카테고리 내 다른 글 보러가기

댓글남기기