반응형

C++ Inheritance (상속) _ 다형성, 업캐스팅, 다운캐스팅

 

다형성(Polymorphism)

  • 생물학에서 유래한 단어, 사전적 의미로 동일한 종에 속하는 생물이라 할지라도 다양한 변이를 가지는 현상을 말함, 즉 여러개의 형태를 갖는다는 것,
  • OOP의 특징중 하나

 

C++ 에서 다형성을 가능하게 해주는 요소들

  1. 상속 관계에서 상위 클래스 또는 하위 클래스로 형변환(up casting, down casting)
  2. 메소드 오버라이딩, 가상함수
  3. 순수 가상 함수
  4. 함수 오버로딩
  5. 연산자 오버로딩

 

상속 관계에서 상위 클래스 또는 하위 클래스로 형변환

  • Up casting : 상속관계에서  하위 클래스가 상위클래스로 형변환 하는 것
  • Down casting : 상속관계에서 업캐스팅했던 상위 클래스 인스턴스가 하위클래스로 형변환 하는 것 

 

Up casting 방법

상위클래스* 클래스이름 = new 하위클래스; 

 

이때 하위클래스 와 상위클래스의 접근 권한이 아래와 같이

 

class 하위클래스 : public 상위클래스

{

}

 

public 으로 되있어야 한다.

 

 

 

 

Down casting 방법

하위클래스* 클래스이름 = (하위클래스*)업캐스팅된상위클래스변수;

 

 

 

 

ex1)

동물

  ↑

고양이

실행결과

40번라인에 있는 animal은 Animal 이 아닌 Cat을 할당 받았고 sound()를 호출해보면 ??? 가 뜬다 즉 하위 클래스를 할당 했더라도, 상위클래스 처럼 쓸 수 있는것이다, 이렇게 상위 클래스의 자료형에 하위 클래스를 할당하는 것을 업캐스팅 이라고 한다,

 

41번 라인은 Down casting 이다, 상위 클래스처럼 쓰이고 있는 하위클래스를 본래의 자료형에 맞게 할당하는것 이때 상위 클래스에서 하위클래스로 형변환 할때는 명시적 형변환이 필요하다, 그뒤 sound()를 호출해보면 Cat::Sound() 함수 정의대로 Mero~ 를 호출 하는것을 볼 수 있다.

 

 

주의점

이러한 형변환 성질을 이용해 여러 객체들을 상속관계로 디자인하고 최상위 클래스들로 Upcasting 한뒤, 객체들을 한꺼번에 하나의 배열에 두고 관리 할 수도 있다, 즉 한번에 for문 하나로 delete 할수도 있게 된다, 그러나 Upcasting 상태에서 delete 하게 되면, 하위클래스의 소멸자는 호출되지 않는다. 

 

 

업캐스팅 한뒤 delete 하게되면 상위 클래스 소멸자만 호출된다.

위 문제를 해결 하기위해서 즉, 하위클래스의 소멸자까지 호출해주게 하기 위해서는 다시 다운캐스팅을 한뒤 delete 해야 한다.

 

 

위처럼, down casting을 하여, delete 해주면 되긴하다, 그러나 만약 상속계층이 cat에서 끝나지 않고 하위클래스가 더 존재한다면?, 그리고 그중에 본래의 하위클래스 형태를 추적하기 힘들다면? 아래와 같은 상황을 보자

 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <iostream>
 
class Animal
{
public:
    Animal()
    {
        std::cout << "Animal 생성자" << std::endl;
    }
 
    ~Animal()
    {
        std::cout << "Animal 소멸자" << std::endl;
 
    }
};
 
class Cat :public Animal
{
 
 
public:
    Cat()
    {
        std::cout << "Cat 생성자" << std::endl;
    }
 
    ~Cat()
    {
        std::cout << "Cat 소멸자" << std::endl;
    }
};
 
 
class SuperCat :public Cat
{
 
 
public:
    SuperCat()
    {
        std::cout << "SuperCat 생성자" << std::endl;
    }
 
    ~SuperCat()
    {
        std::cout << "SuperCat 소멸자" << std::endl;
    }
};
 
class MegaCat :public SuperCat
{
 
 
public:
    MegaCat()
    {
        std::cout << "MegaCat 생성자" << std::endl;
    }
 
    ~MegaCat()
    {
        std::cout << "MegaCat 소멸자" << std::endl;
    }
};
 
 
class SpecialCat :public MegaCat
{
 
 
public:
    SpecialCat()
    {
        std::cout << "SpecialCat 생성자" << std::endl;
    }
 
    ~SpecialCat()
    {
        std::cout << "SpecialCat 소멸자" << std::endl;
    }
};
 
 
 
int main()
{
    Animal* superCat = new SuperCat;
    std::cout << std::endl;
    Animal* superCat_2 = new SuperCat;
    std::cout << std::endl;
    Animal* megaCat = new MegaCat;
    std::cout << std::endl;
    Animal* cat = new Cat;
    std::cout << std::endl;
 
    Animal* animalArr[4= { superCat,superCat_2,megaCat,cat };
 
    for (int i = 0; i < 4; i++)
    {
        delete animalArr[i];
    }
 
 
 
    return 0;
}
cs

실행 결과

예상대로 상위클래스 소멸자만 호출됐다

특히나 위와같이 배열에 담고서 관리할 경우 원래 형태가 어떤 것이었는지 추적하기 어렵다, 이때 한가지 방법이 존재하는데, 가상함수 라는 것이다, 가상함수는 따로 다시 정리하겠지만 여기서는 최상위 클래스의 소멸자에 virtual 이라는 키워드만 붙여주면 된다

 

virtual 키워드는 가상함수를 정의할 때 쓰인다

오직 최상위 클래스 소멸자 앞에 virtual 만 붙였을 뿐인데, 아래와 같은 결과가 나온다

정상적으로 소멸자가 모두 호출된것을 볼 수 있다.

 

즉 최상위 클래스의 소멸자는 가상함수로 만드는게 좋다

 

 

 

정리

  • 상위클래스와 하위클래스간에는 형변환이 가능한데, 이를 Up Casting, Down Casting 이라한다
  • Up Casting 하면 하위 클래스를 상위 클래스처럼 사용 가능하다
  • 어떤 객체들을 최상위 클래스로 Upcasting 하면 하나의 배열로 관리하기 용이하다 그러나 최상위 클래스의 소멸자는 가상함수로 만들어 소멸자가 원활하게 호출되게 해야한다.

 

반응형

+ Recent posts