업캐스팅 (UpCasting)
- C++에서, A 클래스 형 포인터 변수는 A 클래스의 객체 또는 A 클래스를 직, 간접적으로 상속하는 모든 객체를 가리킬 수 있다. (자식 클래스 객체의 주소값을 부모 클래스 포인터 변수에 담아 사용할 수 있다.)
- IS-A 상속 관계의 성립으로 인해서, 부모 클래스를 직, 간접적으로 상속하는 객체를 부모 클래스 객체의 일종으로 간주한다.
- 여러 자식 클래스들의 부모 클래스가 동일 할 경우 해당 부모 클래스에 여러 자식 클래스를 일괄적으로 처리가 가능하다.
#include <iostream>
using namespace std;
class Parent
{
public:
void ShowParent()
{
cout << "Parent class" << endl;
}
};
class Son : public Parent
{
public:
void ShowSon()
{
cout << "Son class" << endl;
}
};
class Daughter : public Parent
{
public:
void ShowDaughter()
{
cout << "Daughter class" << endl;
}
};
void main()
{
Parent* ptr[2] = { new Son(), new Daughter() };
for (int i = 0; i < 2; i++)
{
ptr[i]->ShowParent();
}
//결과
//Parent class
//Parent class
}
※ 업캐스팅 사용시 주의사항
- 위와 같이 업캐스팅하여 함수를 호출할 때 다음과 같은 경우는 컴파일 에러를 일으킨다.
ptr[0]->ShowSon();
ptr[1]->ShowDaughter();
Why?
- 포인터의 형이 존재하는 이유는 메모리를 참조하는 기준이 된다. (C에서 포인터를 했을때 확인했던 개념이다.)
- 즉, Son, Daughter의 객체를 참조하는 ptr은 Person의 메모리 크기만을 참조하기 때문이다.
오버라이딩(Overriding)
- 부모 클래스의 함수를 자식 클래스가 동일한 이름으로 만들어 해당 함수의 내용을 재정의한다.
#include <iostream>
using namespace std;
class Parent
{
public:
void SayHello()
{
cout << "Parent : 'Hello'" << endl;
}
};
class Son : public Parent
{
public:
void SayHello()
{
cout << "Son : 'Hello'" << endl;
}
};
void main()
{
Parent parent;
Son son;
parent.SayHello();
son.SayHello();
//결과
//Parent : 'Hello'
//Son : 'Hello'
}
- 위의 코드를 기준으로 자식 클래스에서 부모 클래스의 함수를 오버라이딩 했다고 해서 업캐스팅으로 자식 클래스의 객체를 참조했을때 오버라이딩 된 함수가 호출되지 않는다.
- 이유는 업캐스팅 주의사항에서 언급했던 것과 동일하다. 그리고 이러한 문제를 해결해주는게 가상(virtual)함수이다.
가상함수(Virtual Function)
- 자식 클래스를 부모 포인터에 업캐스팅 했을 시, 오버라이딩된 함수를 사용하게 해주는 방법
- 부모 클래스에 virtual을 사용해준다.
- virtual 반환형 함수명 (매개변수)
- 소멸자 함수도 virtual을 사용해 줘야한다. (사용하지 않으면 자식 클래스의 소멸자가 호출되지 않는다.)
#include <iostream>
using namespace std;
class Parent
{
public:
virtual void SayHello()
{
cout << "Parent : 'Hello'" << endl;
}
void SayBye()
{
cout << "Parent : 'Bye'" << endl;
}
virtual ~Parent()
{
cout << "Parent Destructor" << endl;
}
};
class Son : public Parent
{
public:
void SayHello()
{
cout << "Son : 'Hello'" << endl;
}
void SayBye()
{
cout << "Son : 'Bye'" << endl;
}
~Son()
{
cout << "Son Destructor" << endl;
}
};
void main()
{
Parent* ptr = new Son();
ptr->SayHello();
ptr->SayBye();
delete ptr;
//결과
//Son : 'Hello'
//Parent : 'Bye'
//Son Destructor
//Parent Destructor
}
- 동적할당을 해제할 때 부모 클래스의 virtual 유무에 따라 자식 클래스의 소멸자가 호출되는지 확인해보는것도 좋다.
※※ 추가 : 멤버, 가상함수의 동작원리
C++ 멤버함수, 가상함수 동작원리
멤버함수 동작원리 - C++의 객체의 멤버변수는 객체 내에 존재하지만 멤버함수는 다음과 같은 관계를 갖는다. 멤버함수는 메모리의 한 공간에 별도로 위치한다. 멤버함수가 정의된 클래스의 모
srdeveloper.tistory.com
'Stack > C++' 카테고리의 다른 글
C++ 멤버함수, 가상함수 동작원리 (0) | 2021.11.17 |
---|---|
C++ 순수 가상함수, 추상 클래스 (0) | 2021.11.11 |
C++ 클래스 상속 (0) | 2021.11.04 |
C++ 함수 오버로딩, Deault 매개변수 (0) | 2021.10.24 |
C++ this 포인터 (0) | 2021.10.24 |