Publish:

태그: , , , , , ,

카테고리:


🤯 언리얼을 하기 위해 C++ 기억 되살리기 프로젝트

캐스팅 (Casting, 형변환)

  • 암시적(implicit) 캐스팅
    • 컴파일러가 형을 변환해 줌
      • 단, 형 변환이 허용되고
      • 프로그래머가 명시적으로 형 변환을 안 할 경우
        1
        2
        
        int number1 = 3;
        long number2 = number1;       // 암시적 캐스팅
        
  • 명시적(Explicit) 캐스팅
    • 프로그래머가 형 변환을 위한 코드 직접 작성
      • static_cast
      • const_cast
      • dynamic_cast (c++98, 모던 c++)
      • reinterpret_cast

1. 스태틱 캐스팅 static_cast

정적 캐스팅.

1
2
3
4
float number1 = 3.f;
int number2 = static_cast<int>(number1);        // <- 이거. 값 변환
Animal* myPet = new Cat(2, "Coco");
Cat* myCat = static_cast<Cat*>(myPet);          // <- 이거. 포인터 변환
    • 두 숫자 형 간의 변환
      • 값을 유지 (단, 반올림 오차는 제외)
      • 이진수 표기는 달라질 수 있음
    1
    2
    3
    4
    5
    6
    
    int number1 = 3;
    short number2 = static_cast<short>(number1);
    
    // int: 3 (0000 0000 0000 0011)
    // 3 (0000 0011)
    // 수는 변하지 않음. 비트 수만 줄어들 뿐...
    
    1
    2
    3
    4
    5
    
    float number1 = 3.f;
    int number2 = static_cast<int>(number1);
    
    // float: 3.f (0100 0000 0100 0000)
    // int: 3 (0000 0000 0000 0011)
    
  • 개체 포인터
    • 변수형 체크 후 부모 클래스를 자식 클래스로 변환
      • 너네 둘이 정말 상속 관계가 있는지? 체크 한다는 것
    • 컴파일 시에만 형 체크 가능
      • 그래서 정적이라는 것
    • 실행 도중 크래시 가능성 있음
    • 예시 1
      1
      2
      3
      4
      5
      6
      
      Animal* myPet = new Cat(2, "Coco");
      Cat* myCat = static_cast<Cat*>(myPet);        // 당연히 됨. 상속 관계니까.
      Dog* myDog = static_cast<Dog*>(myPet);        // 컴파일은 됨.
                                                  // 컴파일러가 체크하는 건 Dog와 Animal간의 상속 관계가 있느냐는 것 뿐.
                                                  // 그래서 캐스팅은 해주는데, myDog은 사실 Cat이니까
      myDog->GetDogHouseName();                     // Dog에 있는 함수 호출 해주면 문제가 생길 수 있음. 함수 자체가 없으니
      
    • 예시 2
    1
    2
    3
    4
    
    Animal* myPet = new Cat(2, "Coco");
    
    House* myHouse = static_cast<House*>(myPet);          // 컴파일 에러
    myHouse->GetAddress();                                // House하고 Cat은 아무런 상속 관계가 없어;
    

2. 리인터프리트 캐스팅 (Reinterpret_cast)

interpret : 해석
즉 재해석 한다는 것.
가장 위험한 캐스팅.

  • 연관 없는 두 포인터 형 사이의 변환을 허용
    • Cat* <-> House*
    • char* <-> int*
  • 포인터와 포인터 아닌 변수 사이의 형 변환을 허용
    • Cat* <-> unsigned int
  • 이진수 표기는 달라지지 않음
    • 컴퓨터는 결과적으로 이진수를 저장할 뿐, 이렇게 해석해달라고 코딩을 하는 것이다.
    • 그래서 이진수는 그대로 둔 채, A를 B로 해석해달라- 고 하는 것.
1
2
3
4
5
6
7
8
9
10
11
int* signedNumber = new int(-10);

unsigned int* unsignedNumber1 = static_cast<unsigned int*>(signedNumber);
// 컴파일 에러. 유효하지 않은 형 변환
// int 포인터를 unsigned int포인터로 읽어버리면, -10이 unsigned int 최대값이 되어버림. 
// 왜냐면 signed int 에서 부호비트(첫번째 비트)는 -일 경우 1로 되어 있으니까...
// 이진수는 그대로 둔 채 해석이 바뀌는 것이므로, 최대값이 된다 이 말임.

unsigned int* unsignedNumber2 = reinterpret_cast<unsigned int*>(signedNumber);
// 컴파일 됨. 하지만 값은 더이상 -10이 아님! 
// unsigned int 의 최대값이 되어버림.
1
2
3
4
Animal* myPet = new Cat(2, "Coco");
unsigned int myPetAddr = reinterpret_cast<unsigned int>(myPet); // <- 이거

cout << "" << hex << myPet << endl;

3. 컨스트 캐스팅 (const_cast)

하지 말아야 할 캐스팅.

  • const cast 로 형을 바꿀 순 없음
  • const 또는 volatile 애트리뷰트를 제거할 때 사용
1
2
3
4
5
void Foo(const Animal* ptr)
{
    Animal* animal = const_cast<Animal*>(ptr);      // <- 이거. const 떼는거.
    animal->SetAge(5);
}
  • 포인터 형에 사용할 때만 말이 됨
    • 값 형은 언제나 복사되니까…
  • 근데 쓰지 마세요
    1
    2
    3
    4
    5
    
    void Foo(const char* name)
    {
      char* str = const_cast<char*>(name);    // const를 뗀 다음 첫 글자를 바꾸고 있는 코드;;
      *str = 'p';                             // 잘못된 코드
    }
    
  • 언제 쓰냐면… 써드파티 라이브러리가 const를 제대로 사용하지 않을 때
    1
    2
    3
    4
    5
    6
    7
    8
    
    void WriteLine(char* ptr);        // 뭔가 별로인 외부 라이브러리
                                      // 출력한다는 함수라고 가정할 때, 내용물이 바뀌지 않아야 하는데
                                      // 그런 보장이 없음
    
    void MyWriteLine(const char* ptr) // 내 프로그램에 있는 함수 새로 작성
    {
      WriteLine(const_cast<char*>(ptr));  // const로 받아서 떼고 넘겨주면 된다
    }
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Animal* myPet = new Cat(2, "Coco");
const Animal* petPtr = myPet;

Animal* myAnimal1 = (Animal*)petPtr;    // const 떼줘! 그냥 Animal* 로 바꿀래!
                                        // 컴파일 가능

Cat* myCat1 = (Cat*)petPtr;             // 나 Cat*으로 바꿀래! 
                                        //컴파일 가능

Animal* myAnimal2 = const_cast<Animal*>(petPtr);    // Animal로 바꿀건데 const_cast 사용할래!
                                                    // 컴파일 가능. const 떼는 거니까 뭐...

Cat* myCat2 = const_cast<Cat*>(petPtr);           // Cat으로 바꿀건데 const_cast 사용할래!
                                                  // 컴파일 에러. 형 변환 허용 안함! static_cast 써야 함.

4. 동적 캐스팅 (dynamic_cast)

  • 실행 중에 형을 판단
  • 포인터 또는 참조 형을 캐스팅 할 때만 쓸 수 있음.
  • 호환되지 않는 자식형으로 캐스팅하려 하면 NULL 반환
    • 따라서 static_cast보다 안전!
  • 하지만 이걸 쓰려면 컴파일 중에 RTTI(실시간 타입정보)를 켜야 한다.
    • 킨다면 typeID를 받아옴.
    • 켜지 않으면 static_cast와 똑같이 작동.
    • 근데 기본적으로 c++ 프로젝트에서 RTTI를 끄는 것이 보통…
      • c++ 프로젝트에선 성능을 중요시 하므로. 이거 느림.
    • 그래서 직접 RTTI를 구현하기도 함.
      • 몇몇 오브젝트에 대해서 부모 클래스에 mType 이런식으로 넣어놓고, GetType() 함수를 만들어서 타입 비교를 하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Animal* myPet = new Cat();

// 컴파일 가능, NULL 반환
Dog* myDog = dynamic_cast<Dog*>(myPet);
// static_cast 했을 땐 아무 문제 없는 코드였는데...
// (물론 Dog에만 있는 함수를 호출한다면 문제가 발생할 수 있음)
// 그러나 동적 캐스팅은.. 너 Dog가 아닌데..? Dog이 니 부모도 아니고..? NULL 반환함.


// 컴파일 가능, 하지만 GetHouseName() 함수 호출 안 됨. if에서 걸려서.
if (myDog != NULL)
{
  myDog->GetHouseName();
}

4-1. static_cast vs dynamic_cast

1
2
3
4
5
6
7
8
9
// static_cast

Animal* myPet = new Cat();

// 컴파일 됨. Animal-Dog 과의 관계만 체크하므로.
Dog* myDog = static_cast<Dog*>(myPet);

// 컴파일 됨. 하지만 어떻게 동작할지 모름. 크래시 날 가능성 있음.
myDog->GetHouseName();
1
2
3
4
5
6
7
8
9
10
11
12
// dynamic_cast

Animal* myPet = new Cat();

// 컴파일 됨. 그리고 언제나 NULL을 반환
Dog* myDog = dynamic_cast<Dog*>(myPet);

// 컴파일 됨. GetHouseName()은 실행되지 않음. if에서 걸려서
if (myDog != NULL)
{
  myDog->GetHouseName();
}

5. 캐스팅 규칙

  • 기본적으로 static_cast를 쓸 것
    • reinterpret_cast<Cat> 대신 static_cast<Cat>을 쓰면 컴파일 에러 확인 가능하니까
  • 필요할 때만 reinterpret_cast를 쓸 것
    • 포인터와 비포인터 사이의 변환
    • 서로 관련 없는 포인터 사이의 변환은 그 데이터형이 맞다고 정말 확신할 때 사용할 것
  • 내가 변경 권한이 없는 외부 라이브러리를 호출할 때만 const_cast를 쓸 것

이슈 및 공부한 것을 기록해두는 개인 블로그 입니다. 댓글, 피드백 환영합니다 🙂

Update:

댓글남기기