Qaupot Blog
Software Engineering, Trip

409. 가상 소멸자

🕐 Sun, 06 Jul 2014 09:00:00 GMT

부모 혹은 자식으로 캐스팅 된 포인터를 삭제할 때

#include <iostream>
class Person
{
public:
    ~Person()
    {
        std::cout << "Person's Destructor" << std::endl;
    }
};

class Student : public Person
{
public:
    ~Student()
    {
        std::cout << "Student's Destructor" << std::endl;
    }
};

int main(int argc, char** argv)
{   
    std::cout << "Delete Person" << std::endl;
    Person* personA = new Person;
    delete personA;

    std::cout << "\nDelete (Student*)Person" << std::endl;
    Person* personB = new Person;
    delete (Student*)personB;

    std::cout << "\nDelete Student" << std::endl;
    Student* studentA = new Student;
    delete studentA;

    std::cout << "\nDelete (Person*)Student" << std::endl;
    Student* studentB = new Student;
    delete (Person*) studentB;
}
Delete Person
Person's Destructor

Delete (Student*)Person
Student's Destructor
Person's Destructor

Delete Student
Student's Destructor
Person's Destructor

Delete (Person*)Student
Person's Destructor

앞선 장에서 가상함수를 다룰때 배운 내용이지만,
멤버 함수가 가상함수로 지정되지 않은 상태에서 부모나 자식으로 캐스팅을 하고나서 메소드를 호출하면
캐스팅된 포인터의 자료형에 따라 메소드를 호출합니다.
이는 소멸자에도 예외는 아니며, 위의 예제의 결과로 확인하실 수 있습니다.

위와 같은 규칙 자체에는 별다른 문제는 없습니다.
포인터를 캐스팅해서 사용했다면, 원래 포인터형으로 다시 캐스팅해서 삭제하면 됩니다.
단지 본래의 타입을 유추해서 캐스팅하고, 제거하는 일련의 작업이 조금 번거로울 뿐입니다.

또한, 소멸자에서는 주로 동적 할당 했던 메모리의 제거 같은 정리 작업이 이루어집니다.
만약 본래 타입으로 캐스팅 한 후 삭제하지 않는다면
메모리 릭(Memory Leak) 혹은 참조 에러가 발생할 가능성이 있습니다.

가상 소멸자(Virtual destructors)

소멸자 역시 가상 멤버 함수로 지정할 수 있습니다.
소멸자를 가상 멤버 함수로 지정하면 위에서 발생한 번거로움을 줄일 수 있습니다.

#include <iostream>
class Person
{
public:
    virtual ~Person()
    {
        std::cout << "Person's Destructor" << std::endl;
    }
};

class Student : public Person
{
public:
    virtual ~Student()
    {
        std::cout << "Student's Destructor" << std::endl;
    }
};

int main(int argc, char** argv)
{
    std::cout << "Delete Person" << std::endl;
    Person* personA = new Person;
    delete personA;

    std::cout << "\nDelete (Student*)Person" << std::endl;
    Person* personB = new Person;
    delete (Student*)personB;

    std::cout << "\nDelete Student" << std::endl;
    Student* studentA = new Student;
    delete studentA;

    std::cout << "\nDelete (Person*)Student" << std::endl;
    Student* studentB = new Student;
    delete (Person*)studentB;
}
Delete Person
Person's Destructor

Delete (Student*)Person
Person's Destructor

Delete Student
Student's Destructor
Person's Destructor

Delete (Person*)Student
Student's Destructor
Person's Destructor

상단의 예제에서 클래스의 소멸자만 가상 멤버 함수로 변경하였습니다.
이에 포인터를 캐스팅 한 후 인스턴스를 삭제해도 생성될 당시 타입의 소멸자가 호출되는 것을 확인할 수 있습니다.

이 블로그는 개인 블로그입니다. 게시글은 오류를 포함하고 있을 수 있지만, 저자는 오류를 해결하기 위해 노력하고 있습니다.
게시글에 별도의 고지가 없는 경우, 크리에이티브 커먼즈 저작자표시-비영리-변경금지 4.0 라이선스를 따릅니다.

This blog is personal blog. published posts may contain some errors, but author doing efforts to clear errors.
If post have not notice of license, it under creative commons Attribution-NonCommercial-NoDerivatives 4.0.