베이스클래스 타입의 변수/참조/포인터가 파생클래스 타입의 객체를 저장/참조/포인팅 할 수 있습니다.
베이스타입의 변수에 파생클래스 타입의 객체를 치환할 수 있습니다.
다형성이란?
베이스클래스 타입의 참조/포인터가 파생클래스 타입의 객체를 참조/포인팅 할 수 있는 것입니다.
Computer 타입의 참조변수가 노트북 객체를 참조할 수 있따는 것입니다.
포인터가 노트북 객체를 포인터할 수 있는 것입니다.
동적 바인딩
만약 computer 타입의 객체일 때 둘 중에 어떤 상황인지 컴파일은 알 수 가 없습니다.
동적바인딩은 코드를 실행하는 단계에서 실제로 가리키고 있는 객체가 뭔지에 따라서 그것의 메서드를 호출해준다는 것입니다. 이것이 virtual method이기 때문에 양쪽 둘 다 있기 때문에 가능한 것입니다.
정리하자면 virtual 메서드인 toString()인 경우에 c++은 동적 바인딩을 하며 실제로 overriding된 toString() 메서드를 호출합니다. 이러한 결정은 실행시간에만 이루어질 수 있으므로 동적 바인딩이라고 합니다. 이것이 다형성(형태가 여러개이다) 이런 의미로 불리는 것입니다.
다형성의 개념
베이스클래스 타입의 참조/포인터는 파생클래스 객체를 참조/가리킬 수 있습니다.
하지만 이 참조/포인터를 통해서 베이스클래스에 존재하는 멤버들만 엑세스할 수 있습니다. 컴파일러가 볼 때는 참조클래스가 베이스 타입이기 때문에 베이스 타입만 엑세스를 할 수 있는 것입니다.
그러나 베이스와 파생 동시에 존재하는 overriding된 virtual 메서드가 문제가 되는 것입니다.
이 경우 동적바인딩이 일어납니다. 즉 실제로 참조/포인팅되고 있는 객체의 메서드가 호출됩니다.
다향성의 장점
- 베이스클래스 포인터 타입의 배열/벡터에 두 종료의 객체를 함께 전달할 수 있습니다.
- 베이스클래스 참조 타입의 매개변수를 받는 함수에게 파생클래스 객체를 전달할 수 있습니다.
int main() {
vector<Shape *> vec;
string stype;
while (true) {
cin >> stype;
if (stype == "R") {
Rect* r = new Rect(cin);
vec.push_back(r);
}
else if (stype == "C") {
Circle* c = new Circle(cin);
vec.push_back(c);
}
else if (stype == "exit")
break;
}
for (auto& s : vec) {
s->print(cout);
cout << endl;
}
return 0;
}
Shape는 기본 클래스이고 Rect와 Circle은 파생클래스에 해당이 됩니다. 각 파생클래스는 기본 클래스의 가상 함수를 재정의합니다.
shape 포인터 s가 실제로 rect또는 circle 객체를 가리키고 있으며 s->print(cout) 호출시 올바른 print가 호출되는 것입니다. 이는 shape클래스의 print함수가 virtual로 선언이 되어있기 때문에 가능한 것입니다.
Pure Virtual Function 과 Abstract Class
virtual function은 파생클래스가 오버라이딩 할 수도 있고 하지않을 수도 있습니다.
하지만 Pure Virtual Function은 반드시 파생클래스가 override를 해야합니다. 그리고 베이스 클래스 즉 자신이 가지고 있는 메서드는 이름만 있을 뿐 실체가 없다는 것을 의미합니다.
virtual double area() = 0;
virtual double minx() = 0;
virtual double maxx() = 0;
virtual double miny() = 0;
virtual double maxy() = 0;
= 0 을 해주면 pure virtual function을 만들어준 것입니다. 실제로 우리가 shape 클래스에서 구현을 할 수는 없습니다. 추상적 개념에 불과한 것이며 shape의 area를 계산할 방법은 없습니다.이것은 서브 클래스에서 실제로 구현합니다.
어떤 클래스가 pure virtual function을 하나라도 가지고 있으면 abstract 클래스가 되며 abstract 클래스의 인스턴스(객체)는 생성할 수 없습니다.
때로는 모든 메서드의 구현을 베이스클래스에서 제공할 수 없는 경우가 있습니다. 예를들어 shape에서 area(), minx(), maxx()등을 구현할 수는 없지만, 다형성의 장점을 누리려면 이 함수를 선언해야 합니다.
이 경우 이 함수들을 베이스클래스의 순수 가상함수로 선언합니다. 순수 가상함수를 하나라도 가진 클래스를 추상 클래스라고 하며, 추상 클래스의 객체는 생성할 수 없습니다.
추상 클래스의 객체는 생성할 수 없지만 추상 클래스도 생성자를 가져야 합니다. 이 생성자는 파생클래스의 객체를 생성할 때 호출하여서는 안됩니다.
추상클래스의 파생클래스가 순수가상함수를 구현하지 않으면 그 자신도 추상클래스가 됩니다.
Virtual destructor
베이스클래스는 일반적으로 가상 소멸자를 정의해야 합니다. 설사 아무 일도 하지 않더라도 가상 소멸자가 필요합니다.
베이스클래스의 소멸자가 가상이 아닌 경우, 파생클래스 객체를 가리키는 베이스 클래스 타입의 포인터에 대해 delete를 실행하면 정의되지 않은 동작이 발생합니다.
'IT 프로그래밍 > 객체지향프로그래밍' 카테고리의 다른 글
객체지향프로그래밍 4 과제 (0) | 2024.06.21 |
---|---|
객체지향프로그래밍 그룹액티비티 7 (0) | 2024.06.19 |
메서드 오버라이딩 (0) | 2024.06.19 |
상속이란? (0) | 2024.06.19 |
컴파일 과정 (0) | 2024.06.18 |