※ Type Casting이란?
변수의 type을 강제로 다른 type으로 변경하는 것으로 자료형간 || 포인터간 형변환시 사용된다.
C/C++은 변수의 type을 변경해 처리해야 하는 경우가 빈번하게 발생한다.
Q. 외부 library 사용시, 인자로 넘겨야할 변수가 char인데 외부의 library가 unsigned char를 사용한다면?
A: 개발자는 unsigned char로 변경해서 넘겨주어야 컴파일 에러가 발생하지 않는다.
int print(unsigned char *str){
cout << str << endl;
}
int main() {
char str[20] = "Hello, world!";
print(str); // type casting이 필요 (Err)
print(reinterpret_cast<unsigned char*>(str));
}
※ Type Casting의 종류
캐스트는 크게 2가지로 나눌 수 있다.
- 묵시적 캐스트 (implicit cast): 캐스트 연산자를 사용하지 않고 형변환이 이뤄지는 경우
- 명시적 캐스트 (explicit cast): 캐스트 연산자를 사용하여 형변환이 이뤄지는 경우
※ static_cast
[사용시기]: 논리적으로 변경가능한 경우
- static_cast의 특성은 묵시적 캐스트와 비슷하다 보면 된다.
- 묵시적 캐스트는 컴파일 시점에서 '무결성'을 검사하는데 이때, '허용'과 '컴파일러에 의한 값 변환' 두 관점이 있다.
- static_cast는 형변환에 의한 타입 확인을 compile 시간에 정적으로 수행한다.
§ 명시적 형변환 §
float f;
int a = static_cast<int>(f);
char *str = static_cast<char*>(f); // Err!
※ const_cast
[사용시기]: 포인터, 참조형에서만 사용가능 const 및 volatile 제거할 때 사용된다!
- const_cast는 상수성이나 volatile(최적화 제외 변수)의 속성을 제거할 때 사용한다.
§ 명시적 형변환 §
int x = 10;
const int *pt_const_x = new int(10);
int *ptx;
ptx = const_cast<int*>(pt_const_x);
*ptx = 20; // 20에서 10으로 값 변경
const int &rt_const_x = x;
int& rtx = const_cast<int&>(rt_const_x);
※ reinterpret_cast
[사용시기]: 명시적 변환과 동작이 동일해 대신 사용된다. 단, const 사용 변환대상은 불가!
- 어떤 포인터 타입도 어떤 포인터 타입으로든 변환이 가능!
- [정수 -> 포인터] 타입도, [포인터 -> 정수]타입으로도 가능하다.
- 강력한 casting 같지만 특수 케이스가 아닌이상 사용을 잘하지 않는 것을 추천 (포인터가 강제 형변환되서)
§ 명시적 형변환 §
int *ptr = new int(10);
char *str;
str = reinterpret_cast<char*>(ptr);
*str = 20; // 10 -> 20으로 값변경
§ const 지정자 사용시, 명시적 형변환 §
const int *ptr = new int(10);
char *str;
str = reinterpret_cast<char*>(const_cast <int*> (ptr));
*str = 20; // 10에서 20으로 값변경
※ dynamic_cast
[사용시기]: class의 포인터, 참조변수간 형변환 시 안전하게 down casting을 위해 사용.
Runtime conversions로 RTTI(Requires Runtime Type Information)
단, parent에 virtual 함수가 존재해야 정상작동!
- run time에 동적으로 상속계층관계를 가로지르거나 down casting시 사용됨
- 기본클래스에 virtual 멤버함수가 하나도 없다면, 다형성을 갖는게 아님(단형성)
∴ 따라서 dynamic_cast는 다형성을 띄지 않은 객체간 변환은 불가능!
§ 명시적 형변환 §
#include <iostream>
using namespace std;
class Blog {
public:
Blog() { cout << "Blog()\n"; };
virtual ~Blog() { cout << "~Blog()\n"; };
void Show() { cout << "This is Blog Class\n"; }
};
class Tistory : public Blog {
public:
Tistory() { cout << "Tistory()\n"; };
virtual ~Tistory() { cout << "~Tistory()\n"; };
void Show() { cout << "This is Tistory Class\n"; }
};
int main(void) {
Blog* pBlog = new Blog();
pBlog->Show();
Tistory* pTistory = dynamic_cast<Tistory*>(pBlog);
if (pTistory == nullptr) { //티스토리 클래스의 포인터가 nullptr이 나올떄.
cout << "Runtime Error\n";
}
else {
pTistory->Show();
}
delete pBlog;
system("pause");
}
※ static_cast VS dynamic_cast
[static_cast]: 정적으로 형변환을 해도 아무 문제가 없다 (= 이미 어떤 녀석인지 알고 있다는 뜻), Fast
[dynamic_cast]: 동적으로 형변환을 시도 해본다는 뜻 (= 이녀석의 타입을 반드시 알아봐야 한다는 뜻), Slow (RTTI)
따라서 dynamic_cast를 이용해 Runtype의 해당 타입을 명확히 알아봐야 하고 (RTTI, Requires Runtime Type Info)
그렇지 않은 경우, static_cast를 이용해 변환 비용을 줄이는 것이 좋다. (동적타입체크를 안해도 되서)
// 비행기에 여러 직군의 사람들이 탑승했다.
// 한 승객이 갑자기 급성 맹장염에 걸려 의사가 급하게 수술을 해야 한다.
class Passenger {...};
class Student : public Passenger{
...
void Study();
};
class Teacher : public Passenger{
...
void Teach();
};
class Doctor : public Passenger{
...
void Treat();
void Operate();
};
int main() {
typedef vector<Passenger *> PassengerVector;
PassengerVector passengerVect;
Passenger* pPS = new Student();
if (pPS){
passengerVect.push_back( pPS );
// 비행기 타자마자 공부한다고 치고~
// pPS가 명확하게 어느 클래스의 인스턴스인지 알고 있다.
// 이 경우엔 굳이 비용이 들어가는 dynamic_cast가 아닌, static_cast를 쓰는게 낫다.
Student* pS = static_cast<Student *>( pPS );
pS->Study();
}
Passenger* pPT = new Teacher();
if ( pPT ){
passengerVect.push_back( pPT );
}
// Doctor 역시 비슷하게 추가.
...
// 응급 환자 발생. passengerVect 중 의사가 있다면 수술을 시켜야 한다.
PassengerVect::iterator bIter(passengerVect.begin());
PassengerVect::iterator eIter(passengerVect.end());
for( ; bIter != eIter; ++bIter ) {
// Passenger 포인터로 저장된 녀석들 중 누가 의사인지 구분해야 한다.
// 런타임 다형성 체크에 의해 Doctor가 아닌 녀석들에 대한 형변환 결과는 NULL
Doctor* pD = dynamic_cast<Doctor *>(*bIter);
if (pD){
pD->Operate();
}
}
}
위 예제는 static_cast와 dynamic_cast를 구분해서 언제 쓰는게 좋은지 알 수 있는 예제이므로 잘 분석해보자.
Q. 만약, 위 코드의 전체 승객 중 의사를 찾아내는 과정에서 dynamic_cast가 아니라, static_cast를 사용하였다면 어떻게 될까?
static_cast는 동적 타입체크를 하지 않고, Student와 Teacher는 Person의 파생 클래스이므로
변환 연산 규정에도 위배되지 않으므로, 그냥 타입 변환이 일어난다.
하지만, 변환 결과는 애초 기대했던 바와 전혀 다릅니다. 실제 Student 클래스 타입이지만,
Doctor 클래스 타입으로 타입 변환이 되면서Doctor 클래스 고유 멤버 함수에 대한 접근이 불가능해진다.
포인터가 가리키는 메모리 내용을 Doctor 클래스에 맞춰서 해석하기에
Student의 내용 중 일부가 Doctor 멤버 필드에 엉뚱하게 들어가거나, 슬라이스 문제가 발생할 수 있다.
다시 말해, 껍데기만 Doctor 클래스이지 내용은 전혀 Doctor의 것이 아니게 되는데,
멤버 필드에 접근시 엉뚱한 값이 들어가 있거나, 런타임 Err가 발생할 수 있다.
'C | C++ > C++' 카테고리의 다른 글
this.code(10)_ template (★★★★★)과 함수객체(functor) (0) | 2022.10.29 |
---|---|
this.code(9)_ smart pointer & type casting_ part 2. (0) | 2022.10.29 |
this.code(7)_ operator overloading_part2. copy function (0) | 2022.10.27 |
this.code(6)_ operator overloading_part1. (0) | 2022.10.25 |
★★this.code(5)_ 상속, 가상함수(동적바인딩 / 오버라이딩 / 추상클래스), 다형성, 다중상속 (0) | 2022.10.25 |