순수 가상 함수
class CObj
{
public:
virtual void Render() = 0;
};
class CPlayer : public CObj
{
public:
virtual void Render() { cout << "Player" << endl; }
};
위와 같은 형식을 가진다.
순수 가상 함수는 오버라이딩을 위해 이름만 제공하는 문법이다.
함수의 몸체는 자식 클래스에 있으며 순수 가상 함수를 가진 클래스를 상속받는 클래스들은
모두 순수 가상 함수의 몸체를 가지고 있어야 한다.
CObj obj; (x)
순수 가상 함수를 하나라도 가진 클래스를 추상 클래스라고 하며 객체 생성이 불가능하다.
가상 소멸자
https://cppking.tistory.com/45
[C++] 가상 함수, 가상 파괴자
void main() { Human H("김사람"); Student S("이학생",1234567); Human *pH; Student *pS; pH=&H; // 당연히 가능 pS=&S; // 당연히 가능 pH=&S; // 가능 // pS=&H; // 에러 pS=(Student *)&H; pS->Intro(); } 자식은 부모 객체의 모든 것을
cppking.tistory.com
상속관계를 가진 클래스의 경우 부모 클래스의 소멸자에 virtual을 붙여주는게 좋다.
부모 타입의 포인터 변수에 자식 객체를 동적 할당하고 delete하면 부모 클래스의 소멸자만 호출된다.
이 때 부모 클래스의 소멸자를 virtual로 선언하면 자식 클래스의 소멸자가 호출된 후 부모 클래스의 소멸자가 호출된다.
캐스팅 연산자
C는 너무 친절해서 별 뚱딴지 같은 캐스팅도 다 해준다. 이러면 문제가 발생할 수 있기 때문에
비논리적인 형 변환을 막기 위한 캐스팅 연산자가 존재한다.
static_cast
char *str="korea";
int *pi;
double d=123.456;
int i;
i=static_cast<int>(d); // 가능
pi=static_cast<int *>(str); // 에러
pi=(int *)str; // 가능
static_cast는 논리적으로 변환 가능한 타입만 변환한다.
포인터를 형변환할 때는 타입을 다른 것으로 바꾸는 것은 허용되지 않으며
상속관계에 있는 클래스일 때만 허용한다.
CObj* pObj = new CObj;
CPlayer* pPlayer = pObj;
CPlayer* pPlayer = static_cast<CPlayer*>(pObj);
CPlayer은 CObj의 자식 클래스이다. static_cast는 다운 캐스팅을 허용한다.
그래서 자식 클래스에만 있는 멤버나 함수에 접근할 경우 문제가 발생한다.
또한 컴파일 시점에 캐스팅하는 정적 캐스팅을 해서 빠르다는 장점이 있지만
잘못된 변환시 dynamic_cast처럼 NULL을 반환하지않고 아무 문제 없는것처럼 처리해 대참사가 일어날 수 있다.
dynamic_cast
기본자료형이나 일반 포인터 대상으로 동작하지 않는다.
클래스가 상속 상태의 포인터여야 하며 부모 클래스에 가상 함수 하나 이상 있어야 한다.
class Parent
{
public:
virtual void PrintMe() { printf("I am Parent\n"); }
};
class Child : public Parent
{
private:
int num;
public:
Child(int anum=1234) : num(anum) { }
virtual void PrintMe() { printf("I am Child\n"); }
void PrintNum() { printf("Hello Child=%d\n",num); }
};
void main()
{
Parent P,*pP,*pP2;
Child C,*pC,*pC2;
pP=&P;
pC=&C;
pP2=dynamic_cast<Parent *>(pC); // 업 캐스팅-항상 안전하다.
pC2=dynamic_cast<Child *>(pP2); // 다운 캐스팅-경우에 따라 다르다.
printf("pC2 = %p\n",pC2);
pC2=dynamic_cast<Child *>(pP); // 캐스팅 불가능
printf("pC2 = %p\n",pC2);
}
dynamic_cast는 다운 캐스팅을 할 때 안전하다고 판단될 때만 허용한다.
안전한 경우는 부모 클래스 포인터 타입이되 실제 자식 객체가 대입되어 있는 경우이다.
예시에서는 pP2가 자식 객체 pC를 업 캐스팅한 것이므로 허용해준다.
하지만 pP처럼 부모 포인터에 부모 객체가 대입되어 있을 경우 캐스팅을 NULL을 리턴해 캐스팅을 금지한다.
https://cppking.tistory.com/52
[C++] RTTI
void func(Parent *p) { p->PrintMe(); ((Child *)p)->PrintNum(); } void main() { Parent p; Child c(5); func(&c); func(&p); } 위 코드에서 Parent 클래스와 Parent의 파생클래스 Child 클래스에만 함수 PrintNum()이 존재한다고 하자. 그
cppking.tistory.com
캐스팅에 의해 객체 타입은 런타임 중 계속 변할 수 있는데
RTTI를 이용해 현재 객체의 타입을 알 수 있다.
dynamic_cast는 런타임 중에 형 변환을 수행하기 때문에(동적 캐스팅) 높은 비용이 발생한다.
'Today I Learned' 카테고리의 다른 글
| 23.02.21 - 함수 객체, 임시 객체, 템플릿 (0) | 2023.02.21 |
|---|---|
| 23.02.20 - 캐스팅 연산자, 인라인 함수, 연산자 오버로딩 (0) | 2023.02.20 |
| 23.02.16 - 정적 바인딩과 동적 바인딩, 가상 함수 (0) | 2023.02.16 |
| 23.02.15 - extern, friend, 상속성 (0) | 2023.02.15 |
| 23.02.14 - 클래스 내 static, 복사 생성자 (0) | 2023.02.14 |