[디딤돌 C++] 38. C++에서의 형 변환

이번에는 C++언어에서 제공하는 형 변환에 관해 살펴보기로 해요.

이 부분은 OOP 특징은 아니지만 앞에서 dynamic_cast를 사용하는 방법을 소개하여 다른 형 변환도 알아보려는 거예요. 먼저 C++ 언어에서도 강제 형변환(명시적 형변환이라고도 부름)을 제공하고 있습니다. 하지만 강제 형변환은 잘못 사용하면 심각한 버그를 유발할 수 있습니다.

다음은 서로 관련이 없는 Man 클래스와 Student 클래스를 정의한 후에 강제 형변환을 사용하는 예제입니다. 컴파일 오류는 발생하지 않지만 프로그램 동작 중에 버그로 런 타임 오류가 발생할 수 있습니다.

//강제 형 변환이 갖는 위험 요소
#include <iostream>
#include <string>
using namespace std;

class Man
{        
    string name;
public:
    Man(string name)
    {        
        this->name = name;
    }
    void View()const
    {
        cout<<"이름:"<<name<<endl;
    }
};

class Student
{
    int num;
    string name;
public:
    Student(int num, string name)
    {
        this->num = num;
        this->name = name;
    }
    void View()const
    {
        cout<<"번호:"<<num<<"이름:"<<name<<endl;
    }
};

int main()
{
    Student *stu = new Student(30,"홍길동");
    Man *man = (Man *)stu; //강제 형변환
    man->View();
    delete stu;
    return 0;
}
강제 형 변환 런 타임 버그

C++에서는 보다 엄격하게 형 변환할 수 있는 방법을 제공하고 있습니다. 앞에서 다루었던 dynamic_cast는 일반화 관계에서 하향 캐스팅을 제공했었죠. 이 외에 static_cast, const_cast, reinterpret_cast를 제공하고 있습니다.

먼저 static_cast는 C언어의 강제 형변환과 유사합니다. 기반 클래스와 파생 클래스 사이의 형 변환 뿐만 아니라 기본 자료 형식 사이에서도 형 변환을 할 수 있습니다. 하지만 이에 관한 모든 책임은 개발자에게 있다는 것을 잊지 마세요.

다음은 하향 캐스팅을 dynamic_cast를 이용하지 않고 static_cast를 이용하였습니다. dynamic_cast를 사용하려면 가상 메서드를 갖고 있어야 하지만 static_cast는 가상 메서드를 갖고 있지 않아도 가능합니다.

//static_cast
#include <iostream>
#include <string>
using namespace std;
class Man
{        
    string name;
public:
    Man(string name)
    {        
        this->name = name;
    }
    void View()const
    {
        cout<<"이름:"<<name<<endl;
    }
};

class Student:public Man
{
    int num;
    string name;
public:
    Student(int num, string name):Man(name)
    {
        this->num = num;
        this->name = name;
    }
    void View()const
    {
        cout<<"번호:"<<num<<"이름:"<<name<<endl;
    }
    void Study()
    {
        cout<<name<<" 공부하다."<<endl;
    }
};

int main()
{
    Man *man = new Student(30,"홍길동");
    Student *stu = static_cast<Student *>(man);
    stu->Study();
    delete man;
    return 0;
}

▷ 실행 결과

홍길동 공부하다.

다음은 기본 형식 사이에 static_cast를 사용한 예제입니다.

//기본 형식 간에 static_cast
#include <iostream>
using namespace std;

int main()
{
    int a = 3;
    double d;

    d = static_cast<double>(a);
    cout<<d<<endl;
    return 0;
}

▷ 실행 결과

3

하지만 언제나 static_cast를 할 수는 없습니다. 예를 들어 string 형식과 int 형식 사이에서는 static_cast를 할 수 없습니다.

//static_cast 를 할 수 없는 예
#include <iostream>
#include <string>
using namespace std;

int main()
{
    int a = 3;
    string name;

    name = static_cast<string>(a);//형 변환 오류
    cout<<name<<endl;
    return 0;
}
static_cast 오류

static_cast는 다양한 형태에서 형 변환을 할 수 있지만 모든 책임은 개발자의 몫임을 항상 잊지 마세요.

이번에는 const_cast를 알아봅시다.

const 포인터 변수는 가리키는 값을 변경할 수 없죠. const_cast는 const 포인터 변수가 가리키는 값을 변경할 수 있게 형 변환합니다. 만약 값을 변경하지 않겠다는 의미로 const 포인터 변수를 입력 인자로 받은 후에 const_cast로 값을 변경할 수 있다는 것입니다. 이러한 사용은 프로그램의 신뢰성이 떨어질 수 있습니다.

//const_cast
#include <iostream>
using namespace std;

void View(const int *input)
{    

    int *temp = const_cast<int *>(input);    

    *temp = 9;
    cout<<"=== 가리키는 값 출력 ==="<<endl;
    cout<<"*temp:"<<*temp<<endl;
    cout<<"*input:"<<*input<<endl;    
}
int main()
{
    int value = 10;

    View(&value); 
    return 0;
}

▷ 실행 결과

=== 가리키는 값 출력 ===

*temp:9

*input:9

마지막으로 reinterpret_cast를 알아봅시다. reinterpret_cast는 강제 형변환처럼 거의 모든 형 변환을 가능하게 해 줍니다. 예를 들어 flat * 를 int *로 형 변환을 요청하는 것을 허용합니다. 이러한 변환에서는 형 변환한 곳의 값을 float 형식이 아닌 int이라고 해석하여 값을 출력합니다.

다음은 float 형식 변수 -12.625가 메모리에 어떻게 기록하는지 확인하기 위해 reinterpret_cast를 사용한 예제 코드입니다.

//reinterpret_cast
#include <iostream>
#include <iomanip>
#include <string>

using namespace std;

int main()
{
    
    float f=-12.625;
    float *pf = &f;
    int *a;

    a = reinterpret_cast<int *>(pf);
    cout<<hex<<(*a)<<endl;
    return 0;
}

▷ 실행 결과

c14a0000

dynamic_cast는 다형성이 갖는 단점을 해결하기 위해 사용하지만 다른 형 변환은 극약 처방이 필요한 응급 상황이 아니면 사용을 자제해 주세요.