티스토리 뷰
모기를 잡으려면 모기약을 써야지 사람 잡는 약을 쓰면 되나
C스타일의 연산자는 어떤 것이든 다 변환한다.
사람이 실수를 해도 컴파일러는 이를 잡아낼 수가 없다.
#include <iostream>
using namespace std;
class Car
{
private:
int fuelGauge;
public:
Car(int fuel):fuelGauge(fuel)
{}
void ShowCarState() { cout<<"잔여 연료량: " << fuelGauge << 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;
ptruck2->ShowTruckState();
return 0;
}
pcar1은 Truck 객체고, Truck형으로 참조하는 것은 자연스럽다.
pcar2는 Car 객체이기 때문에, Truck 형 변환은 자연스럽지 않다.
'기초 클래스의 포인터 형을 유도 클래스의 포인터 형으로 바꾸는 것은 일반적인 형 변환이 아니다.'
C++에서 이런 문제의 해결을 위해 4가지 형 변환 연산자를 제공한다.
-static_cast
-const_cast
-dynamic_cast
-reinterpret_cast
dynamic_cast:상속관계에서의 안전한 형 변환
dynamic_cast<T>(expr)
<>사이에 변환하고자 하는 자료형을 입력하고, 객체의 포인터 또는 참조형이 와야 하며, ()사이에는 변환의 대상이 와야한다.
안전한 형 변환이란?
'상속관계에 놓여 있는 두 클래스 사이에서 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로 형 변환하는 경우'
#include <iostream>
using namespace std;
class Car
{
private:
int fuelGauge;
public:
Car(int fuel):fuelGauge(fuel)
{}
void ShowCarState() { cout<<"잔여 연료량: " << fuelGauge << 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); //컴파일에러
Car* pcar2 = new Car(120);
Truck* ptruck2 = dynamic_cast<Truck*>(pcar2);//컴파일에러
Car* pcar3 = new Truck(70,150);
Car* ptruck3 = dynamic_cast<Car*>(ptruck3);//컴파일ok
return 0;
}
Car->Truck(X)
Truck->Truck (X)
Truck->Car(O)
pcar2는 안되는 것 알겠는데, pcar1은 왜 안되나요?
-> 이에 static_cast를 사용
static_cast: A타입에서 B타입으로
static_cast<T><expr)
기초->유도(O)
유도->기초(O)
유도->유도(O) , 이런 경우는 별로 할 일이 없다.
모두 되기에, 조심해야한다.
static_cast:기본 자료형 간 변환
double->int 등에서 쓰인다.
그렇다면 원래 C 자료형과 차이는 무엇인가?
int main(void
{
const int num=20;
int*ptr=int* # //const 상수의 포인터는 const 포인터가 되어야 한다.
*ptr=30; //상수 num이 실제로 변경된다..
cout<<*ptr<<endl; //30출력
float*adr=(float*)ptr; //int형 데이터를 float형으로 변환한다.
cout<<*adr<<endl; //저장된 데이터를 float형으로 해석한다.
}
기본자료형 간 다른 포인터형 변환이 안된다.
static은 const 제거 불가.
const_caㄴt: const의 성향을 제거하라.
const_cast<T>(expr)
reinterpret_cast: 상관없는 자료형으로의 형 변환
reinterpret_cast<T>(exper)
클래스가 상속으로 관계를 맺은 것도 아닌 두 클래스에 사용할 수 있다.
int main(void)
{
SimpleCar* car= new car;
BestFriend* fren = reinterpret_cast<BestFriend*>(car)
}
#include <iostream>
using namespace std;
int main(void)
{
int num = 0x010203;
char* ptr = reinterpret_cast<char*>(&num);
for (int i = 0; i < sizeof(num); i++)
cout << static_cast<int>(*(ptr + i)) << endl;
return 0;
}
출력결과
3
2
1
0
int형이 char형으로, char형이 int형으로 바뀌었다.
dynamic_cast 두 번째 이야기: Polymorphic 클래스 기반의 형 변환
-상속관계에 놓여있는 두 클래스 사이에서, 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로 형 변환할 경우에는 dynamic_cast 연산자를 사용한다.
-반대로, 상속관계에 놓여있는 두 클래스 사이에서, 기초 클래스의 포인터 및 참조형 데이터를 유도 클래스의 포인터 및 참조형 데이터로 형 변환할 경우에는 static_cast 연산자를 사용한다.
기초클래스의 포인터 및 참조형 데이터를 유도 클래스의 포인터 및 참조형으로 형 변환을 허용한다.
'기초 클래스가 Polymorphic 클래스일 경우'
ploymorphic 클래스: 하나 이상의 가상함수를 지니는 클래스
#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* simPtr = new SoComplex;
SoComplex* comPtr = dynamic_cast<SoComplex*>(simPtr);
comPtr->ShowSImpleInfo();
return 0;
}
결가
SoComplex Derived Class
유도클래스가 호출되었다. virtual이 없으면 에러가 발생한다.
정말 마지막까지 골치 아프게 하는군요.
그렇다면 dynamic_cast와 static_cast의 차이점은 무엇일까?
int main(void)
{
SoSimple*SimPtr=new SoComplex;
SoComplex*comptr= dynamic_cast<SoComplex*>(simptr);
}
기초->유도로 변경 성공
int main(void)
{
SoSimple*SimPtr=new SoSimple;
SoComplex*comptr= dynamic_cast<SoComplex*>(simptr);
}
유도가 기초가리키는 것 불가!
dynamic은 이처럼 위의 상항을 막아주지만, static은 그렇지 못하다.
프로그래머가 정의하지 않아도 발생하는 예외가 있다.
bad_cast 예외
#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);
comRef.ShowSImpleInfo();
}
catch (bad_cast expt)
{
cout << expt.what() << endl;
}
return 0;
}
결과
Bad dynamic_cast!
예외가 나타날 경우를 생각해 예외처리 문장을 설정해야한다.
'프로그래밍 > 윤성우의 열혈 C++' 카테고리의 다른 글
윤성우의 열혈 C++ 프로그래밍 리뷰 (1) | 2022.03.18 |
---|---|
Chapter 15 예외처리(Exception Handling) (0) | 2022.03.17 |
14 템플릿(Template) 2 (0) | 2022.03.17 |
Chapter 13 템플릿(Template) 1 (0) | 2022.03.15 |
Chapter 12 String 클래스의 디자인 (0) | 2022.03.15 |
- Total
- Today
- Yesterday
- 윤성우
- 파이썬
- 인프런
- 일문따
- EBS
- 뇌와행동의기초
- 일본어문법무작정따라하기
- 류근관
- c++
- 심리학
- 열혈프로그래밍
- 데이터분석
- 일본어
- K-MOOC
- 통계학
- 오블완
- 티스토리챌린지
- 사회심리학
- stl
- 보세사
- 여인권
- 백준
- 회계
- 인지부조화
- 코딩테스트
- jlpt
- C/C++
- C
- Python
- 통계
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |