18.4 Virtual destructors, virtual assignment, and overriding virtualization

주홍영·2022년 3월 21일
0

Learncpp.com

목록 보기
175/199

https://www.learncpp.com/cpp-tutorial/virtual-destructors-virtual-assignment-and-overriding-virtualization/

비록 c++이 default destructor를 제공하고 있지만 가끔씩 우리는 직접 만들어야할 필요가 있다
(특히 메모리를 deallocate 해야하는 상황에)
만약 우리가 inheritance를 다루고 있다면 우리는 반드시 우리의 destructor를 virtual로 만들어야 한다
다음의 예시를 보자

#include <iostream>
class Base
{
public:
    ~Base() // note: not virtual
    {
        std::cout << "Calling ~Base()\n";
    }
};

class Derived: public Base
{
private:
    int* m_array;

public:
    Derived(int length)
      : m_array{ new int[length] }
    {
    }

    ~Derived() // note: not virtual (your compiler may warn you about this)
    {
        std::cout << "Calling ~Derived()\n";
        delete[] m_array;
    }
};

int main()
{
    Derived *derived { new Derived(5) };
    Base *base { derived };

    delete base;

    return 0;
}

main 함수에서 base는 Base pointer 이므로 base가 delete되면 프로그램은 Base destructor가 virtual인지 볼 것이다. 위 프로그램에서는 Base destructor가 virtual이 아니므로 오직 Base destructor만 호출할 것이다

그러나 우리는 Derived destructor 또한 호출해야한 한다
위의 경우에는 m_array가 해제되지 않을 것이다.
우리는 위에서 발생하는 문제를 Base's desturctor를 virtual로 설정해서 해결할 수 있다

#include <iostream>
class Base
{
public:
    virtual ~Base() // note: virtual
    {
        std::cout << "Calling ~Base()\n";
    }
};

class Derived: public Base
{
private:
    int* m_array;

public:
    Derived(int length)
      : m_array{ new int[length] }
    {
    }

    virtual ~Derived() // note: virtual
    {
        std::cout << "Calling ~Derived()\n";
        delete[] m_array;
    }
};

int main()
{
    Derived *derived { new Derived(5) };
    Base *base { derived };

    delete base;

    return 0;
}

위와 같이 virtual로 설정해주면 의도한대로 프로그램이 동작할 것이다

Rule

Whenever you are dealing with inheritance, you should make any explicit destructors virtual.

만약 우리가 virtual destructor가 필요한 상황이지만
해당 클래스에서 딱히 특별한 구현이 필요치 않은 경우 다음과 같이 작성할 수 있다

virtual ~Base() = default; // generate a virtual default destructor

default destructor의 기능만 필요하지만 virtual이 필요한 경우 위처럼 작성한다

Virtual assignment

일단은 이 토픽은 넘어간다

Ignoring virtualization

매우 드물게 우리는 function의 virtual을 무시하고 싶을 수 있다
다음의 예시를 보자

class Base
{
public:
    virtual ~Base() = default;
    virtual const char* getName() const { return "Base"; }
};

class Derived: public Base
{
public:
    virtual const char* getName() const { return "Derived"; }
};

우리는 derived object를 받은 Base pointer로 Base::getName()을 호출하고 싶을 수 있다
이때 다음과 같이 한다

#include <iostream>
int main()
{
    Derived derived;
    const Base &base { derived };
    // Calls Base::GetName() instead of the virtualized Derived::GetName()
    std::cout << base.Base::getName() << '\n';

    return 0;
}

위와 같이 base.Base::getName()이라고 scope specifier operator를 이용해 Base의 getName()을 호출한다

Should we make all destructors virtual?

신입 프로그래머들이 흔히하는 질문이다. 혹시 모를 memory leak을 위해서는 모든 destructor에 virtual marking을 하는게 좋긴하다. 하지만 꼭 그래야만 할까?

쉽게말하면 그렇게 하라. performance penalty가 따르겠지만 다른것을 모두 고려하면 하는게 가장 적절하다

다음의 추가적인 설명은 생략한다

profile
청룡동거주민

0개의 댓글