static_cast<> | 컴파일 시 형변환 |
dynamic_cast<> | 런타임 시 형변환 |
const_cast<> | 상수형 포인터에서 const 제한을 제거 |
reinterpret_cast<> | C의 형변환과 유사 |
static_cast
static_cast는 형변환이 적절한지 컴파일 시 체크한다.
즉, 적절한 형변환이 아니면 강제로 형식을 변환하지 않는다.
링크에서는 TEST class에서 (int) 변환에 대한 type casting을 정의해줬으므로 적절환 형변환이 가능했고,
static_cast로 형변환이 적절하다고 판단할 수 있다.
static_cast는 파생 클래스의 형식을 기본 형식(부모 클래스)으로 캐스팅하는 것을 허용한다.
이전 예제에서 아래의 코드를 추가해서 실행해보자.
#include <stdio.h>
#include <iostream>
using namespace std;
class FOOD
{
private:
int price = 0;
public:
FOOD() { printf("FOOD created\n"); }
virtual ~FOOD() { printf("FOOD deleted\n"); }
virtual void printName() { cout << "FOOD Class" << endl; }
virtual void printPrice() { cout << this->price << endl; }
void printLine() { cout << "=================" << endl; }
};
class SNACK : public FOOD
{
private:
int price = 1000;
public:
SNACK() { printf("SNACK created\n"); }
~SNACK() { printf("SNACK deleted\n"); }
/* override 시 compile error */
void printName(int value) { cout << "SNACK Class, value : " << value << endl; }
void printPrice() { cout << this->price << endl; }
/* override 시 compile error */
void printLine() { cout << "------------------------" << endl; }
};
class MILK : public FOOD
{
private:
int price = 500;
public:
MILK() { printf("MILK created\n"); }
~MILK() { printf("MILK deleted\n"); }
void printName() { cout << "MILK Class" << endl; }
void printPrice() { cout << this->price << endl; }
};
int main(void)
{
FOOD* foodList[3];
foodList[0] = new FOOD();
foodList[1] = new MILK();
foodList[2] = new SNACK();
printf("\n---- TEST START ----\n\n");
for (int i = 0; i < 3; i++)
{
foodList[i]->printName();
foodList[i]->printPrice();
foodList[i]->printLine();
putchar('\n');
}
printf("\n\n---- static_cast TEST ----\n\n");
// 적절한 형변환
SNACK* snack1 = static_cast<SNACK*>(foodList[2]);
//snack1->printName(); // SNACK에는 printName(void)가 없다.
snack1->printName(5);
snack1->printPrice();
snack1->printLine();
// downcasting 자식 클래스 타입 - 부모 클래스 인스턴스
SNACK* snack2 = static_cast<SNACK*>(foodList[0]);
// snack->printName(); //foodList -> SANCK으로 변환, 이제 FOOD가 아니므로 printName(void)는 불가
snack2->printName(5);
snack2->printPrice(); // 비정상적인 값 출력
snack2->printLine();
// MILK* milk = static_cast<MILK*>(snack); // MILK는 SNACK이 아니므로 compile error
// upcasting 부모 클래스 타입 - 자식 클래스 인스턴스
FOOD* food = static_cast<FOOD*>(snack2); // SNACK을 다시 FOOD 로 변환
//food->printName(5); // FOOD이므로 printName(int)는 불가
food->printName();
food->printPrice();
food->printLine();
return 0;
}
foodList[2]는 FOOD 타입이지만 SNACK 객체이다.
즉, foodList[2]가 가르키는 것이 SNACK인 것이 명확하므로, 파생 형식에 대한 포인터로 형변환이 가능하다.
→ static_cast가 이것을 컴파일 타임에 허용한다.
FOOD* foodList[3];
foodList[2] = new SNACK();
// 적절한 형변환
SNACK* snack1 = static_cast<SNACK*>(foodList[2]);
//snack1->printName(); // SNACK에는 printName(void)가 없다.
snack1->printName(5);
snack1->printPrice();
snack1->printLine();
FOOD (부모)를 SNACK (자식)으로 변형 하였다. - downcasting
가능은 하지만 자식 클래스의 멤버 함수(printPrice)를 부르면 엉뚱한 값이 출력된다.
따라서 사용을 자제하는 것이 좋다.
FOOD* foodList[3];
foodList[0] = new FOOD();
// downcasting 자식 클래스 타입 - 부모 클래스 인스턴스
SNACK* snack = static_cast<SNACK*>(foodList[0]);
// snack->printName(); //foodList -> SANCK으로 변환, 이제 FOOD가 아니므로 printName(void)는 불가
snack->printName(5);
snack->printPrice(); // 비정상적인 값 출력
snack->printLine();
MILK와 SNACK은 부모/자식 관계가 아니다. 따라서 static_cast가 컴파일에 에러를 일으킨다.
MILK* milk = static_cast<MILK*>(snack); // MILK는 SNACK이 아니므로 compile error
SNACK (자식)을 FOOD (부모)로 변형하였다. - upcasting
// upcasting 부모 클래스 타입 - 자식 클래스 인스턴스
FOOD* food = static_cast<FOOD*>(snack); // SNACK을 다시 FOOD 로 변환
//food->printName(5); // FOOD이므로 printName(int)는 불가
food->printName();
food->printPrice();
food->printLine();
dynamic_cast
동적으로 생성된 객체가 기본 형식인 경우, 구체적으로 어떤 객체에 대한 인스턴스인지 확인할 때 사용한다.
(RTTI, Run-Type Type Information, Identification)
위의 예제에서 main문을 아래와 같이 수정하자.
int main(void)
{
bool test = false;
FOOD* food = nullptr;
if (test) food = new MILK();
else food = new SNACK();
MILK* milk = dynamic_cast<MILK*>(food);
if (milk != NULL) printf("test = true, I'm milk!\n");
else
{
SNACK* snack = dynamic_cast<SNACK*>(food);
if (snack != NULL) printf("test = false, I'm snack\n");
}
return 0;
}
test가 true인 경우 / false인 경우 출력 결과는 아래와 같다.
부모 타입의 FOOD에 대해 객체는 test = true / false에 따라 MILK / SNACK이 된다.
dynamic_cast가 실패하는 경우 NULL을 출력하기 때문에, 현재 객체의 인스턴스가 무엇인지 확인할 수 있다.
좀 더 자세히 확인하기 위해 다음의 결과를 보자.
int main(void)
{
bool test = false;
FOOD* fd = new FOOD();
SNACK* sn = new SNACK();
MILK* mk = new MILK();
printf("\n\n --------- dynamic test --------- \n\n");
FOOD* dc1 = dynamic_cast<FOOD*>(fd);
FOOD* dc2 = dynamic_cast<SNACK*>(fd);
FOOD* dc3 = dynamic_cast<MILK*>(fd);
printf("dc1 fd - fd : %x\n", dc1);
printf("dc2 sn - fd : %x\n", dc2);
printf("dc3 mk - fd : %x\n", dc3);
putchar('\n');
FOOD* dc4 = dynamic_cast<FOOD*>(sn);
FOOD* dc5 = dynamic_cast<SNACK*>(sn);
FOOD* dc6 = dynamic_cast<MILK*>(sn);
printf("dc4 fd - sn : %x\n", dc4);
printf("dc5 sn - sn : %x\n", dc5);
printf("dc6 mk - sn : %x\n", dc6);
putchar('\n');
FOOD* dc7 = dynamic_cast<FOOD*>(mk);
FOOD* dc8 = dynamic_cast<SNACK*>(mk);
FOOD* dc9 = dynamic_cast<MILK*>(mk);
printf("dc7 fd - mk : %x\n", dc7);
printf("dc8 sn - mk : %x\n", dc8);
printf("dc9 mk - mk : %x\n", dc9);
putchar('\n');
return 0;
}
SNACK, MILK의 인스턴스는 FOOD가 아니므로 NULL을 반환하였다. (dc2, dc3)
MILK / SNACK도 서로가 서로의 인스턴스가 아니므로 NULL을 반환하였다. (dc6, dc8)
하지만 MILK / SNACK은 FOOD이므로 dynamic_cast이 가능하다.
이제 FOOD* fd = new FOOD()를 new SNACK()으로 변경해보자.
FOOD* fd = new SNACK();
fd는 SNACK 인스턴스지만 FOOD*로 dynamic casting 하는데 문제가 없다. (dc1)
그리고 fd는 SNACK 인스턴스이므로 SNACK*으로 dynamic casting이 가능해졌다. (dc2)
최종 코드는 아래와 같다.
#include <stdio.h>
#include <iostream>
using namespace std;
class FOOD
{
private:
int price = 0;
public:
FOOD() { printf("FOOD created\n"); }
virtual ~FOOD() { printf("FOOD deleted\n"); }
virtual void printName() { cout << "FOOD Class" << endl; }
virtual void printPrice() { cout << this->price << endl; }
void printLine() { cout << "=================" << endl; }
};
class SNACK : public FOOD
{
private:
int price = 1000;
public:
SNACK() { printf("SNACK created\n"); }
~SNACK() { printf("SNACK deleted\n"); }
/* override 시 compile error */
void printName(int value) { cout << "SNACK Class, value : " << value << endl; }
void printPrice() { cout << this->price << endl; }
/* override 시 compile error */
void printLine() { cout << "------------------------" << endl; }
};
class MILK : public FOOD
{
private:
int price = 500;
public:
MILK() { printf("MILK created\n"); }
~MILK() { printf("MILK deleted\n"); }
void printName() { cout << "MILK Class" << endl; }
void printPrice() { cout << this->price << endl; }
};
int main(void)
{
bool test = false;
FOOD* fd = new SNACK();
SNACK* sn = new SNACK();
MILK* mk = new MILK();
printf("\n\n --------- dynamic test --------- \n\n");
printf("\n --------- FOOD type fd = instance SNACK --------- \n\n");
FOOD* dc1 = dynamic_cast<FOOD*>(fd);
FOOD* dc2 = dynamic_cast<SNACK*>(fd);
FOOD* dc3 = dynamic_cast<MILK*>(fd);
printf("dc1 fd - fd : %x\n", dc1);
printf("dc2 sn - fd : %x\n", dc2);
printf("dc3 mk - fd : %x\n", dc3);
putchar('\n');
FOOD* dc4 = dynamic_cast<FOOD*>(sn);
FOOD* dc5 = dynamic_cast<SNACK*>(sn);
FOOD* dc6 = dynamic_cast<MILK*>(sn);
printf("dc4 fd - sn : %x\n", dc4);
printf("dc5 sn - sn : %x\n", dc5);
printf("dc6 mk - sn : %x\n", dc6);
putchar('\n');
FOOD* dc7 = dynamic_cast<FOOD*>(mk);
FOOD* dc8 = dynamic_cast<SNACK*>(mk);
FOOD* dc9 = dynamic_cast<MILK*>(mk);
printf("dc7 fd - mk : %x\n", dc7);
printf("dc8 sn - mk : %x\n", dc8);
printf("dc9 mk - mk : %x\n", dc9);
putchar('\n');
return 0;
}
'개발 > C, C++' 카테고리의 다른 글
C++ 스마트 포인터 : auto_ptr (0) | 2021.10.10 |
---|---|
C++ 스마트 포인터 (Smart Pointer) (0) | 2021.10.03 |
C++ - virtual / override 키워드 (0) | 2021.08.25 |
C++ - 가상 소멸자 (Virtual Destructor) (0) | 2021.08.23 |
C++ - explicit 키워드 (0) | 2021.08.22 |
댓글