우선 오버라이딩을 보겠습니다.
#include <iostream>
class Parent {
public:
Parent()
{
std::cout << "부모클래스 생성자" << std::endl;
}
~Parent()
{
std::cout << "부모클래스 소멸자" << std::endl;
}
void test()
{
std::cout << "부모 test()" << std::endl;
}
};
class Child : public Parent{
public:
Child()
{
std::cout << "자식클래스 생성자" << std::endl;
}
~Child()
{
std::cout << "자식클래스 소멸자" << std::endl;
}
void test()
{
std::cout << "자식 test()" << std::endl;
}
};
int main()
{
std::cout << " === 부모 클래스 생성 ===" << std::endl;
Parent p;
p.test();
std::cout << " === 자식 클래스 생성 ===" << std::endl;
Child c;
c.test();
return 0;
}
위 코드를 실행하면
부모클래스에서 test를 호출하면 당연히 부모클래스의 test가 실행돼서 부모 test()가 출력되고, 부모클래스를 상속받는 자식클래스에서 test를 호출하면, 자식클래스의 test가 부모클래스의 test를 오버라이드해서 자식클래스의 test가 호출됩니다.
코드를 약간 바꿔보겠습니다.
#include <iostream>
class Parent {
public:
Parent()
{
std::cout << "부모클래스 생성자" << std::endl;
}
~Parent()
{
std::cout << "부모클래스 소멸자" << std::endl;
}
void test()
{
std::cout << "부모 test()" << std::endl;
}
};
class Child : public Parent{
public:
Child()
{
std::cout << "자식클래스 생성자" << std::endl;
}
~Child()
{
std::cout << "자식클래스 소멸자" << std::endl;
}
void test()
{
std::cout << "자식 test()" << std::endl;
}
};
int main()
{
Parent p;
Child c;
// c를 가리키는 부모클래스 포인터 p_c
Parent* p_c = &c;
p_c->test();
return 0;
}
위 코드를 실행해보면
자식클래스를 가리키는 포인터인데 부모클래스의 test를 실행했습니다.
타입이 다른데 어떻게 이런 대입이 가능하냐고 물을 수 있습니다. 부모클래스와 자식클래스는 엄연히 다른 클래스이니까요. 하지만 자식클래스가 부모클래스를 상속받고 있기때문에 이와같은 일이 가능합니다. 자식클래스의 객체 c도 부모클래스 객체이기때문에 부모클래스 객체를 가리키는 포인터가 c를 가리켜도 상관없다는 것입니다.
하지만 포인터 p_c는 엄연한 부모클래스를 가리키는 포인터입니다. 따라서, p_c의 test를 실행한다면 p_c는 부모클래스의 test를 실행시킵니다. 이러한 형태의 캐스팅을 업 캐스팅 이라고 합니다.
그렇다면 다운 캐스팅도 있는지 해보겠습니다.
#include <iostream>
class Parent {
public:
Parent()
{
std::cout << "부모클래스 생성자" << std::endl;
}
~Parent()
{
std::cout << "부모클래스 소멸자" << std::endl;
}
void test()
{
std::cout << "부모 test()" << std::endl;
}
};
class Child : public Parent{
public:
Child()
{
std::cout << "자식클래스 생성자" << std::endl;
}
~Child()
{
std::cout << "자식클래스 소멸자" << std::endl;
}
void test()
{
std::cout << "자식 test()" << std::endl;
}
};
int main()
{
Parent p;
Child c;
Child* p_p = &p;
p_p->test();
return 0;
}
위 코드를 실행시키면 아래와 같은 오류 메세지가 나옵니다.
이런 오류가 나오는 이유는 간단합니다. Child* 포인터가 부모클래스 객체를 가리키고 p_p->test() 하게 된다면 자식클래스의 test 함수가 호출되어야하는데 이는 불가능합니다. 왜냐하면 p_p가 가리키는 객체는 부모클래스 객체인데 p_p는 자식클래스 포인터이므로 Child의 test를 호출하고싶은데 그 정보가 없습니다. 따라서 이와 같은 문제를 막기 위해서 컴파일러 상에서 함부로 다운 캐스팅 하는 것을 금지하고 있습니다.
다음 상황도 보겠습니다.
#include <iostream>
class Parent {
public:
Parent()
{
std::cout << "부모클래스 생성자" << std::endl;
}
~Parent()
{
std::cout << "부모클래스 소멸자" << std::endl;
}
void test()
{
std::cout << "부모 test()" << std::endl;
}
};
class Child : public Parent{
public:
Child()
{
std::cout << "자식클래스 생성자" << std::endl;
}
~Child()
{
std::cout << "자식클래스 소멸자" << std::endl;
}
void test()
{
std::cout << "자식 test()" << std::endl;
}
};
int main()
{
Parent p;
Child c;
Parent* p_p = &c;
Child* p_c = p_p;
p_c->test();
return 0;
}
위 코드를 실행해보면 전 상황과 똑같은 에러메세지가 나옵니다.
하지만 우리는 p_p가 가리키는 것이 부모클래스 객체가 아니라 자식클래스 객체라는 사실을 알고 있습니다. 그렇기 때문에 비록 Parent* 포인터를 다운 캐스팅 함에도 불구하고 p_p가 실제로는 자식클래스 객체를 가리키기 때문에 p_c에 p_p를 대입해도 전혀 문제가 없습니다. 이를 위해서 아래처럼 강제적으로 타입 변환을 하면 됩니다.
Child* p_c = static_cast<Child*>(p_p);
실행하면 아래처럼 정상적으로 출력되는 것을 확인할 수 있습니다.
'C++' 카테고리의 다른 글
상속과 가상 함수 (0) | 2020.10.29 |
---|---|
가상 함수(virtual 키워드)와 다형성 (0) | 2020.10.29 |
상속 (Inheritance) (0) | 2020.10.27 |
C++ 에서의 캐스팅(형변환) (0) | 2020.10.27 |
연산자 오버로딩(전위/후위 증감 연산자) (0) | 2020.10.27 |