2 minute read

알고리즘 문제 풀이를 할 때 내가 의도한대로 형변환이 되지 않고 어디까지 형변환이 되고 있는건지 이해가 잘 가지 않았다.

C++ 형변환에 대한 이해가 부족하다고 느껴져서 간단하게 정리하였다. C++ 의 상속 전반에 대한 공부가 부족하여 완전히 이해하지는 못하였다. (씹어먹는 C++에서 상속을 건너뛰었더니 이런 불상사가.. )

C 스타일 형변환 (오래된 형변환)

C에서는 아래와 같은 방법으로 형변환을 할 수 있었다.

int n = 3;
float n1 = (float)n;
float n2 = float(n);

위와 같이 형변환하는 방식을 C++에서 C 스타일 형변환 또는 오래된 형변환이라고 한다. 이렇게 형변환이 구분되어 있다는 것은 C++에서 형변환하는 방법이 따로 존재한다는 뜻일거다.

C++이 제공하는 형변환

C++에서는 무려 네 가지의 형변환 방법을 제공하고 있다.

  • static_cast
  • dynamic_cast
  • reinterpret_cast
  • const_cast

예를 들면 아래와 같이 형변환을 할 수 있다.

double d = 3.14;
int num = static_cast<int>(d);

구체적으로 각 case를 살펴보자.

static_cast

static(정적)이라는 이름에서 알 수 있듯이 static_cast는 컴파일 단계에서 형변환이 가능한지 컴파일러가 체크할 수 있다. C++에 정의된 기본 자료형간의 형변환에서 사용된다. (밑에 언급할 dynamic_cast는 사용자가 정의한 클래스 간의 형변환 가능)

double d = 3.14;
int n1 = static_cast<int>(d);
int n2 = dynamic_cast<int>(d);  // 컴파일 에러, 기본 자료형 형변환은 불가

다시 말해서 기본 자료형에 대해서는 사용할 수 있는 선택자가 static_cast로 정해져있다는 것이다. static_cast와 dynamic_cast 중에서 어떤 것을 사용할지는 부모-자식 클래스 간의 포인터/참조 형식의 형변환의 경우에서만 고민하면 된다.

static_cast의 경우에는

부모 클래스의 참조/포인터 형식 -> 자식 클래스의 참조/포인터 (O)
자식 클래스의 참조/포인터 -> 부모 클래스의 참조/포인터 형식 (O)

위와 같은 형변환을 허용한다.

말로만 보면 이해가 안가니 코드를 살펴보자.

#include <iostream>
#include <string>

class Human{
	int age;
	public:
		Human(int age) : age(age) {}
		void showInfo() {
            std::cout << "내 나이는 " << age << "입니다" << std::endl;
        }
};

class Man : public Human {
    std::string gender;
    public:
    	Man(int age, std::string gender) : Human(age), gender(gender) {}
    	void showInfo() {
            Human::showInfo();
            std::cout << "내 성별은 " << gender << "입니다" << std::endl;
        }
};

Human과 Man 클래스를 정의하였다. Man은 Human을 상속 후 추가로 gender 변수를 가지고 있고 showInfo도 오버라이딩 하였다. static_cast를 통해 두 클래스의 형변환을 할 수 있다.

int main() {
    Human* human1 = new Man(20, "남자");
    Man* man1 = static_cast<Man*>(human1);
    man1->showInfo();

    Human* human2 = new Human(30);
    Man* man2 = static_cast<Man*>(human2);
    man2->showInfo();

    return 0;
}
내 나이는 20입니다
내 성별은 남자입니다
내 나이는 30입니다
내 성별은 입니다

정상적으로 컴파일이 되고 출력 결과는 위와 같다. 그런데 첫 번째 케이스의 형 변환은 이해가 되는데, 아래 케이스는 결과만 봐도 뭔가 이상하지 않은가? static_cast는 상속관계에서의 형변환이 가능은 하지만 위와 같이 안전한 형변환이라고 하기에는 무리가 있다.

dynamic_cast

dynamic_cast는 static_cast와 달리 컴파일 단계가 아닌 런타임 단계에서 안정성 검사를 진행한다.

(기초적인 알고리즘 풀이에서도 static_cast 외에 다른 것들이 사용될라나..? )

어쨌든 dynamic_cast는 자식 클래스의 참조/포인터 형식 -> 부모 클래스의 참조/포인터 형식으로의 형변환을 허용한다.

reinterpret_cast

위 내용은 생략한다.. 간단하게 언급하면 포인터/참조 타입에 상관없이 무조건 강제적인 형변환을 해야할 때 사용하는 것이다.

const_cast

const_cast는 const 성질을 제거하기 위해 사용하는 연산자이다.

const std::string str1 = "hello";
std::string str2 = const_cast<std::string>(str2);
std::cout << str2;

const가 아닌 변수를 매개변수로 받는 것으로 정의된 함수에 const형을 전달하지 못하기 때문에 이때 const_cast를 사용할 수 있다.

Tags:

Categories:

Updated: