ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Effective C++] 7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자
    C_C++ 프로그래밍/Effective C++ 2019. 9. 8. 17:29


          로재의 개발 일기      

    파생 클래스에 소멸자를 쓰면??

    다들 파생 클래스를 만들어보신 경험이 있을겁니다.

    이때 만약에 기반 클래스 포인터로 파생 클래스를 가리키게 된다면?

    파생 클래스의 소멸자가 정상적으로 작동할까요?


    답은 아닙니다.


    이번 항목에서는 virtual 소멸자 및 함수에 대해서 말하고 있습니다.


      nonVirtual.cpp

    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
    #include<iostream>
     
    class Base{
            public:
                    Base(){
                            std::cout << "Base class Create" << '\n';
                    }
                    ~Base(){
                            std::cout << "Base class delete" << '\n';
                    }
    };
     
    class Derived1 : public Base{
            public:
                    Derived1(): Base(){
                            std::cout << "Derived1 class Create" << '\n';
                    }
                    ~Derived1(){
                            std::cout << "Derived1 class delete" << '\n';
                    }
    };
     
    class Derived2 : public Base{
            public:
                    Derived2(): Base(){
                            std::cout << "Derived2 class Create" << '\n';
                    }
                    ~Derived2(){
                            std::cout << "Derived2 class delete" << '\n';
                    }
    };
     
    int main(){
            Base* p1 = new Derived1;
            Base* p2 = new Derived2;
            delete p1;
            delete p2;
     
            return 0;
    }
     
    cs


    실행 동작은 다음과 같았습니다.

    파생 클래스의 소멸자는 동작하지 못했고

    마찬가지로 기반 클래스의 what 함수만 동작했네요.


    C++의 규정에 의하면

    기본 클래스 포인터를 통해 파생 클래스 객체가 삭제될 때 그 기본 클래스에 비가상 소멸자가 들어 있다면 프로그램 동작은 미정의 사항이라고 적혀있습니다.

    (대개 기본 클래스의 소멸자가 실행됩니다)


    그렇다면 어떻게 해야할까요?


    비기 virtual을 써봅시다.


      virtual.cpp

    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
    #include<iostream>
     
    class Base{
            public:
                    Base(){
                            std::cout << "Base class Create" << '\n';
                    }
                    virtual ~Base(){
                            std::cout << "Base class delete" << '\n';
                    }
                    virtual void what(){
                            std::cout << "Base's what" << '\n';
                    }
    };
     
    class Derived1 : public Base{
            public:
                    Derived1(): Base(){
                            std::cout << "Derived1 class Create" << '\n';
                    }
                    ~Derived1(){
                            std::cout << "Derived1 class delete" << '\n';
                    }
                    void what(){
                            std::cout << "Derived1 what" << '\n';
                    }
    };
     
    class Derived2 : public Base{
            public:
                    Derived2(): Base(){
                            std::cout << "Derived2 class Create" << '\n';
                    }
                    ~Derived2(){
                            std::cout << "Derived2 class delete" << '\n';
                    }
                    void what(){
                            std::cout << "Derived2 what" << '\n';
                    }
    };
     
    int main(){
            Base* p1 = new Derived1;
            Base* p2 = new Derived2;
            p1->what();
            p2->what();
            delete p1;
            delete p2;
     
            return 0;
    }
     
    cs


    단순히 virtual 키워드만 추가 시켜주었는데요.


    파생 클래스의 소멸자 및 함수까지
    완벽하게 동작하는 것을 볼 수 있습니다.




    하지만 항상 가상 소멸자를 선언하는 것은 아닙니다.

    1. STL 컨테이너 (string, vector, list....)

    이 컨테이너 내부에는 virtual 소멸자가 존재하지 않기 때문에 STL을 사용하여 나만의 클래스를 만들기는 힘들겠군요..


    2. 가상 함수가 하나라도 없는 클래스

    가상 함수가 하나라도 존재하지 않으며, 그것이 다형성을 가지지 않는 경우 ( 단편적인 클래스 사용)는

    굳이 사용하여 (클래스 크기가 커집니다 약 2배) 낭비할 필요가 없는거죠.




    참고

    1.vptr (가상 함수 테이블 포인터)

    이는 가상 함수의 주소, 즉 포인터들의 배열을 가리키고 있으며, 가상 함수를 하나라도 가지고 있는 클래스는 반드시 그와 관련된 vtbl(가상 함수 테이블)을 사용하게 됩니다.

    이를 통해서 32비트 아키텍처라면, 크기가 64비트(int 2개)에서 96비트(int 2개, vptr하나)로 ..

    64비트 아키텍처라면 96비트(int 2개)에서 128비트로 커질 수 있습니다.

    >> 아무튼 크기가 커집니다. 무분별한 사용은 자제합시다.


    ※ 본 글은 개인 포트폴리오 혹은 공부용으로 사용하기 때문에, 무단 복사 유포는 금지하지만, 개인 공부 용도로는 얼마든지 사용하셔도 좋습니다


    반응형
Designed by Tistory.