일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- LG Aimers
- LG Aimers 4th
- deep learning
- 머신러닝
- 딥러닝
- LG
- Classification
- regression
- supervised learning
- 해커톤
- 티스토리챌린지
- PCA
- 분류
- 지도학습
- 회귀
- GPT-4
- 오블완
- OpenAI
- LLM
- ChatGPT
- AI
- Machine Learning
- gpt
- Today
- Total
SYDev
C++ Chapter 16-1 : C++에서의 형 변환 연산 본문
C 스타일 형 변환 연산자
#include <iostream>
using namespace std;
class Car
{
private:
int fuelGuage;
public:
Car(int fuel) : fuelGuage(fuel)
{ }
void ShowCarState() { cout<<"잔여 연료량: "<<fuelGuage<<endl; }
};
class Truck : public Car
{
private:
int freightWeight;
public:
Truck(int fuel, int weight) : Car(fuel), freightWeight(weight)
{ }
void ShowTruckState()
{
ShowCarState();
cout<<"화물의 무게: "<<freightWeight<<endl;
}
};
int main(void)
{
Car * pcar1=new Truck(80, 200);
Truck * ptruck1=(Truck *)pcar1; //기초 클래스의 포인터 형을 유도 클래스의 포인터 형으로 형 변환하는 것은 일반적이지 않음
ptruck1 ->ShowTruckState();
cout<<endl;
Car * pcar2=new Car(120);
Truck * ptruck2=(Truck *)pcar2; //포인터가 가리키는 객체가 Car 객체이므로, 문제가 있는 문장
ptruck2 ->ShowTruckState();
return 0;
}
잔여 연료량: 80
화물의 무게: 200
잔여 연료량: 120
화물의 무게: 0
- 위 예제와 같이 문제가 있는 형 변환문도 C 스타일 형 변환 연산으로 형 변환을 진행해버리면 컴파일 오류 없이 컴파일이 진행되어 버린다.
- C++에서는 총 4개의 연산자를 추가로 제공하면서 용도에 맞는 형 변환 연산자의 사용을 유도한다.
dynamic_cast: 상속관계에서의 안전한 형 변환
dynamic_cast<T>(expr)
- <>사이에 변환하고자 하는 자료형의 이름(객체의 포인터 또는 참조형)
- ()사이에 변환의 대상
dynamic_cast 형 변환 연산자는 상속관계에 놓여 있는 두 클래스 사이에서 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로 형 변환하는 경우 사용한다.
#include <iostream>
using namespace std;
class Car
{
private:
int fuelGuage;
public:
Car(int fuel) : fuelGuage(fuel)
{ }
void ShowCarState() { cout<<"잔여 연료량: "<<fuelGuage<<endl; }
};
class Truck : public Car
{
private:
int freightWeight;
public:
Truck(int fuel, int weight) : Car(fuel), freightWeight(weight)
{ }
void ShowTruckState()
{
ShowCarState();
cout<<"화물의 무게: "<<freightWeight<<endl;
}
};
int main(void)
{
/*
Car * pcar1=new Truck(80, 200);
Truck * ptruck1=dynamic_cast<Truck*>(pcar1); //compile error
Car * pcar2=new Car(120);
Truck * ptruck2=dynamic_cast<Truck*>(pcar2); //compile error
*/
Truck * ptruck3=new Truck(70, 150);
Car * pcar3=dynamic_cast<Car*>(ptruck3); //compile ok
return 0;
}
static_cast: A 타입에서 B 타입으로
static_cast<T>(expr)
static_cast 형 변환 연산자는 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로 형 변환하는 경우 뿐만 아니라, 기초 클래스의 포인터 및 참조형 데이터를 유도 클래스의 포인터 및 참조형 데이터로 형 변환하는 경우도 허용한다.
#include <iostream>
using namespace std;
class Car
{
private:
int fuelGuage;
public:
Car(int fuel) : fuelGuage(fuel)
{ }
void ShowCarState() { cout<<"잔여 연료량: "<<fuelGuage<<endl; }
};
class Truck : public Car
{
private:
int freightWeight;
public:
Truck(int fuel, int weight) : Car(fuel), freightWeight(weight)
{ }
void ShowTruckState()
{
ShowCarState();
cout<<"화물의 무게: "<<freightWeight<<endl;
}
};
int main(void)
{
Car * pcar1=new Truck(80, 200);
Truck * ptruck1=static_cast<Truck*>(pcar1); //compile ok
ptruck1->ShowTruckState();
cout<<endl;
Car * pcar2=new Car(120);
Truck * ptruck2=static_cast<Truck*>(pcar2); //compile ok! but..
ptruck2->ShowTruckState();
return 0;
}
잔여 연료량: 80
화물의 무게: 200
잔여 연료량: 120
화물의 무게: 0
static_cast 형 변환 연산자는 보다 많은 형 변환을 허용하는 대신에, 위 예제와 같이 잘못된 상황에서의 형 변환도 허용하기 때문에 제한적인 상황에서 책임질 수 있는 경우에만 사용하는 것이 좋다.
static_cast 형 변환 연산자는 다음과 같이 기본 자료형 간의 형 변환도 가능
double result=static_cast<double>(20)/3;
static_cast와 C 스타일 형 변환 연산자의 차이점
- static_cast 연산자는 C 스타일 형 변환 연산자와 달리, '기본 자료형 간의 형 변환'과 '클래스의 상속관계에서의 형 변환'만 허용
- C 스타일 형 변환 연산자는 다음과 같은 일반적이지 않은 형 변환도 허용한다.
int main(void)
{
const int num=20;
int * ptr=(int*)# //const 상수의 포인터는 const 포인터
*ptr=30; //const 상수 num의 값이 실제로 변경됨
cout<<*ptr<<endl; //30이 출력
float * adr=(float*)ptr; //int형 포인터를 float형으로 변환
cout<<*adr<<endl; //저장된 데이터를 float형으로 해석해서 출력
}
const_cast: const의 성향 삭제
const_cast<T>(expr)
const_cast 형 변환 연산자는 포인터와 참조자의 const 성향을 제거하는 형 변환을 목적으로 하는 연산자이다.
#include <iostream>
using namespace std;
void ShowString(char* str)
{
cout<<str<<endl;
}
void ShowAddResult(int& n1, int& n2)
{
cout<<n1+n2<<endl;
}
int main(void)
{
const char * name="Lee Sung Ju";
ShowString(const_cast<char*>(name)); //const char*형 데이터를 char*형으로 형 변환
const int& num1=100;
const int& num2=200;
ShowAddResult(const_cast<int&>(num1), const_cast<int&>(num2)); //const int&형 데이터를 int&형으로 형 변환
return 0;
}
Lee Sung Ju
300
reinterpret_cast: 상관없는 자료형으로의 형 변환
const_cast<T>(expr)
reinterpret_cast 형 변환 연산자는 포인터를 대상으로 하는, 그리고 포인터와 관련이 있는 모든 유형의 형 변환을 허용한다.
#include <iostream>
using namespace std;
int main(void)
{
int num=0x010203;
char * ptr=reinterpret_cast<char*>(&num); //int형 정수에 바이트 단위 접근을 위해 int형 포인터를 char형 포인터로 형 변환
for(int i=0; i<sizeof(num); i++)
cout<<static_cast<int>(*(ptr+i))<<endl; //바이트 단위 데이터를 문자가 아닌 정수의 형태로 출력하기 위해 char형 데이터를 int형으로 변환
return 0;
}
3
2
1
0
+ 위 예제는 원래는 4바이트 크기인 int를 char로 바꿔서 1바이트씩 쪼개서 사용하는 예제라고는 하는데 아직은 잘 모르겠다.. 아무튼 reinterpert_cast 형 변환이 포인터를 대상으로 하는 형 변환이라는 것에 집중하자.
reinterpret_cast는 다음과 같은 형 변환도 가능하다.
int main(void)
{
int num=72;
int* ptr=#
int adr=reinterpret_cast<int>(ptr); //주소 값을 정수로 변환
cout<<"Addr: "<<adr<<endl; //주소 값 출력
int* rptr=reinterpret_cast<int*>(adr); //정수를 다시 주소 값으로 변환
cout<<"value: "<<*rptr<<endl; //주소 값에 저장된 정수 출력
. . . .
}
dynamic_cast 두 번재 이야기: Polymorphic 클래스 기반의 형 변환
- 기초 클래스가 Polymorphic 클래스인 경우에 한하여, dynamic_cast 연산자도 기초 클래스의 포인터 및 참조형 데이터를 유도 클래스의 포인터 및 참조형 데이터로의 형 변환을 허용한다.
- Polymorphic 클래스: 하나 이상의 가상함수를 지니는 클래스
이와 관련된 다음 예제를 살펴보자.
#include <iostream>
using namespace std;
class SoSimple //Polymorphic 클래스
{
public:
virtual void ShowSimpleInfo()
{
cout<<"SoSimple Base Class"<<endl;
}
};
class SoComplex : public SoSimple
{
public:
void ShowSimpleInfo() //이것 역시 가상함수
{
cout<<"SoComplex Derived Class"<<endl;
}
};
int main(void)
{
SoSimple * simPtr=new SoComplex; //simPtr이 SoComplex 객체를 가리키는 포인터이며, SoComplex가 comPtr이 가리켜도 되는 객체이기 때문에 형 변환이 가능
SoComplex * comPtr=dynamic_cast<SoComplex*>(simPtr); //기초 클래스인 SoSimple형 포인터 변수 simPtr을 유도 클래스인 SoComplex형 포인터로 형 변환
comPtr->ShowSimpleInfo();
return 0;
}
SoComplex Derived Class
위 상황이 가능해지는 이유는 main함수를 살펴보면 알 수 있다. 만약에 simPtr이 SoSimple 객체를 가리키는 포인터 변수였다면, 형 변환의 결과로 NULL 포인터가 반환된다.
이와 관련하여 위 예제에서 main 함수만 다음과 같이 바꾸면 출력되는 값이 바뀐다.
int main(void)
{
SoSimple * simPtr=new SoSimple;
SoComplex * comPtr=dynamic_cast<SoComplex*>(simPtr);
if(comPtr==NULL)
cout<<"형 변환 실패"<<endl;
else
comPtr->ShowSimpleInfo();
return 0;
}
형 변환 실패
- 결론적으로, dynamic_cast는 안정적인 형 변환을 보장하지만, 컴파일 시간이 아닌 프로그램이 실행중인 동안에 안전성을 검사한다. 그렇기 때문에, static_cast에 비해 실행 속도가 느리다.
- 반면에, static_cast는 실행시간에 안전성 검사를 별도로 진행하지 않는다. 그렇기 때문에, dynamic_cast에 비해 실행 속도가 빠르다.
bad_cast 예외
- bad_cast 예외는 dynamic 연산자를 이용한 형 변환의 과정에서 발생할 수 있는 예외이다.
#include <iostream>
using namespace std;
class SoSimple
{
public:
virtual void ShowSimpleInfo()
{
cout<<"SoSimple Base Class"<<endl;
}
};
class SoComplex : public SoSimple
{
public:
void ShowSimpleInfo()
{
cout<<"SoComplex Derived Class"<<endl;
}
};
int main(void)
{
SoSimple simObj;
SoSimple& ref=simObj;
try
{
SoComplex& comRef=dynamic_cast<SoComplex&>(ref);
/*
ref가 참조하는 대상이 SoSimple 객체이기 때문에
SoComplex 참조형으로의 형 변환은 안전하지 못하다.
또한, 참조자를 대상으로는 NULL을 반환할 수 없기 때문에
이런 상황에서는 bad_cast 예외가 발생한다.
*/
comRef.ShowSimpleInfo(); //예외의 발생으로 실행되지 못한다.
}
catch(bad_cast expt)
{
cout<<expt.what()<<endl; //what 함수는 예외의 원인을 문자열의 형태로 반환한다.
}
return 0;
}
std::bad_cast
참고자료
- 윤성우, <윤성우의 열혈 C++ 프로그래밍>, 오렌지미디어, 2010.05.12
'Programming Lang > C++' 카테고리의 다른 글
C++ Chapter 15-5 : 예외처리와 관련된 또 다른 특성들 (0) | 2023.08.01 |
---|---|
C++ Chapter 15-4 : 예외상황을 표현하는 예외 클래스의 설계 (0) | 2023.08.01 |
C++ Chapter 15-3 : Stack Unwinding(스택 풀기) (0) | 2023.08.01 |
C++ Chapter 15-2 : C++의 예외처리 메커니즘 (0) | 2023.08.01 |
C++ Chapter 15-1 : 예외상황과 예외처리의 이해 (0) | 2023.08.01 |