C++

업 캐스팅과 다운 캐스팅

종황이 2020. 10. 29. 02:37

우선 오버라이딩을 보겠습니다.

#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