C++ - casting

mohadang·2022년 9월 24일
0

C++

목록 보기
10/48
post-thumbnail

암시적 캐스팅

  • 컴파일러가 알아서 처리

명시적 캐스팅

  • static_cast

    • 컴파일 시점에 코드를 봐서 캐스팅이 불가능 할 것 같은 경우 컴파일 에러 발생
    • 가능한 경우 캐스팅 해줌
  • const_cast

    • const 변수를 일반 변수로 캐스팅, const 를 벗겨버리는 캐스팅
  • dynamic_cast(c++ 98)

    • RTTI 옵션 켜져야 함.
    • 캐스팅 불가능 하면 NULL 반환
    • 런타임 중에 리플렉션을 이용해서 개체를 까서 캐스팅이 가능한지 판단
  • reinterpret_cast

    • C처럼 무조건 캐스팅 해줌

C 스타일 캐스팅의 문제점

int score = (int)someValue;

  • 개발자는 위의 코드를 무슨 의도로 넣었을까??
  • C 스타일 캐스팅은 너무 무적이다. 무었이든지 허용이 된다.
  • C++ 스타일의 4개의 캐스팅중 하나를 한다.
    • 명확하지 못함
  • 명백한 실수를 컴파일러가 캐치하지 못함
    • C++ 캐스팅이 좀더 세분화 되어 있어서 문제를 잡는데 명확하다.
  • 어셈블리 단에서는 C++과 C에서의 캐스팅은 같다.

정적 캐스팅

  • 컴파일 시점에 컴파일러가 캐스팅이 가능할지 불가능할지 판단하고 불가능 하면 컴파일 에러를 발생 시킴.
  • C
    • int number = (int)value;
  • C++
    • int number = static_cast<int>(value);
    • 컴파일 중에 타입이 결정이 된다.
    • 두 숫자형간의 변환
      • 가능하면 값을 유지(반올림, 오차는 예외)
      • 캐스팅 과정에서 값이 변할 수 있음
        • float number = 3.f;
          • 0100 0000 0100 0000
        • int number2 = static_cast(number);
          • 0000 0000 0000 0011

객체 포인터

  • 변수형 체크 후 베이스 클래스를 파생 클래스로 변환
  • 컴파일 시에만 형 체크 가능
House* dog = static_cast<House*>(myPet);// Compile Error  
// C 스타일었다면 가능했다...
  • 실행 도중에 여전히 크래시가 날 수 있음
Animal* myPet = new Cat();
Cat* cat = static_cast<Cat*>(myPet);// Compile OK, 다운 캐스팅 가능
Dog* dog = static_cast<Dog*>(myPet);// Compile OK, 다운 캐스팅 가능
dog->GetDogHouseName();//런타임 에러 발생, 동적 타입은 Cat이다.

리인터프리트 캐스팅

  • C
Animal* myPet = new Cat(2, "Coco");
unsigned int address = (unsigned int)myPet;
  • C++
Animal* myPet = new Cat(2, "Coco");
unsigned int address = reinterpret_cast<unsigned int>(myPet);
  • reinterpret : 통역, 재해석
  • C++에서 가장 위험한 casting...
  • 연관없는 두 포인터간의 캐스팅 가능
    char <-> House
    char <-> int
  • 포인터와 포인터 아닌 타입과 캐스팅 가능
    char* <-> unsinged int
  • '이진수 표기가 달라지지 않음'
    • A형의 이진수 표기를 그냥 B형인 것처럼 해석
    • 그래서 포인터와 아닌 타입간의 캐스팅이 가능 한 듯.
  • static_cast vs reinterpret_cast
int* signedNum = new int(-10);
// Compile Error 유효하지 않은 값  
unsigned int* unsignedNum = static_cast<unsigned int*>(signedNum); 
// Compile 허용
unsigned int* unsignedNum = reinterpret_cast<unsigned int*>(signedNum);
  • 리인터프리트 캐스팅은 C스타일 캐스팅과 별반 다르지 않음.
  • 리인터프리트 캐스팅을 자주 사용하는 경우는 주소를 실제 숫자형으로 변할때 많이 사용 할 것 같다.
  • 이진수가 달라지지 않기에 개발자가 데이터 형 변환시 좀더 확실한 예측을 할 수 있을 것 같다.

const 캐스팅

  • C
void Foo(const Animal* ptr)
{
  // 'Bad Code'
  Animal* animal = (Animal*)ptr;
  animal->SetAge(5);
}
  • C++
void Foo(const Animal* ptr)
{
  // 'Bad Code'

  Animal* animal = const_cast<Animal*>ptr;
  animal->SetAge(5);
}
  • const 를 벗겨버린다.
  • 함수의 시그니처를 무시하기에 나쁜 코드이다.
  • 위험한 캐스팅이라기 보다는 '하지 말아야 하는 캐스팅' 이다.
  • const_cast로 형을 빠꿀수는 없음
  • const 와 volatile 애트리뷰트를 제거 할 수 있음.
  • 포인터 형에 사용할 때만 말이됨
    • 값 형은 복사니까 const라는 것 자체가 무의미
  • const_cast를 사용한다는 것 자체부터 무언가 코드를 잘못 짜고 있다고 보아도 된다.
  • 그렇다면 const_cast를 사용해야 할 때는
void WriteLine(char* ptr)// 뭔가 별로인 외부 라이브러리
void MyWriteLine(const char* ptr)// 우리 프로그램에 있는 함수
{
  // 외부 라이브러리를 사용하기 위해서는 어쩔 수 없이 const를 벗겨야 하는 경우
  WriteLine(const_cast<char*>(ptr));   
}

// 근데 위의 코드도 사실 사용되어서는 안되는 코드이다.
// 차라리 함수 제작자 한테 허락맏고 우리 함수의 const를 빼고 받는것이 좋을 것이다.

동적 캐스팅(dynamic casting)

  • C
    Cat* myCat = (Cat*)myPet;

  • C++
    Cat* myCat = dynamic_cast<Cat*>(myPet);

  • static 은 컴파일 중에 캐스팅... 컴파일중에 경고 발생... 그렇다면 dynamic은 실행중에 캐스팅이 된다.

  • C

Animal* myPet = new Cat();
Dog* dog = (Dog*)myPet;// compie OK
dog->GetDogHouseName();// compie 되지만 실행하면 크래시 발생
  • C++
Animal* myPet = new Cat();
Dog* dog = dynamic_cast<Dog*>(myPet);// compie OK, return NULL

if(dog)<--NULL이 아닐때만...
{
  dog->GetDogHouseName();
}
  • 실행중에 캐스팅을 한다.
  • 호환되지 않는 자식형으로 캐스팅 할때는 NULL을 반환한다.
  • 포인터 또는 참조형을 캐스팅 할 때만 사용 가능.
  • "But 이걸 사용하려면 RTTI(Real time type information)이라는 컴파일 옵션을 켜야함".
    • C++ 프로젝트에서는 기본적으로 끈다. 익셉션과 마찬가지로
    • 왜 끌까? 이유는 성능, C++은 성능때문에 사용하는데 이 RTTI를 킬만큼 성능 과부하를 견딜 수 가 없음.
  • dynamic_cast, static_cast는 실제로는 동일하게 동작, 그러나 RTTI가 켜져 있으면 다름.
  • 정말 RTTI를 사용하고 싶다면 모든 클래스 타입이 아닌 일부 클래스 타입에 대해서만 사용하고 싶을 것이다. 그럴경우 직접 클래스 안에 타입 ID정보를 나타낼 수 있는 멤버를 추가하여 직접 구현한다.

C++ 의 베스트 프랙티스

  • 규칙 : 제일 안전한 것 부터 시작해서 조금씩 위험한 것으로 빠진다.
    1. 기본적으로는 static_cast를 사용하자
    1. static_cast로 힘들면 reinterpret_cast를 사용
    • 포인터와 비포인터 사이 변환
    • 서로 연관이 없는 포인터 사이의 변환은 그 데이터 형이 '정말 확신'이 들었을 때만 사용
    1. 내게 변경 권한이 없는 외부 라이브러리를 호출할 때만 const_cast를 사용
  • 이렇게 사용하면 static_cast만 보다가 몇몇개의 reinterpret_cast 가 나오면 주의깊게 보기에 코드 검사 하는 입장에서도 보기 좋다.
profile
mohadang

0개의 댓글