[완료]virtual 소멸자에 대한 질문

bluekyu의 이미지

virtual 소멸자에 대해서 질문이 있는데, 포럼이나 다른 곳에서 원하는 답변을 얻지 못해서 질문 올려봅니다.

class A{
    char a;
public:
    A(){a = new char[10];}
    ~A(){delete []a;}
};
 
class B: A{
    char b;
public:
    B(){b = new char[10];}
    ~B(){delete []b;}
};
 
int main(){
   A* x = new B;
   delete x;
   return 0;
}

위와 같이 일반적으로 기반 클래스인 A가 파생 클래스 B 객체를 참조하고 있으면 ~A()에 virtual을 써주어야 ~B()가 실행된다는 것은 알고 있습니다.

그런데 제가 공부하고 있는 책에 있는 문제를 코딩하다 보니까 파생 클래스에서 아래처럼 소멸자가 필요 없는 경우가 생겼습니다.

class A{
    char a;
public:
    A(){a = new char[10];}
    ~A(){delete []a;}
};
 
class B: A{ // B 클래스 자체로는 소멸자를 따로 생성할 필요가 없음
    ...
public:
    B(){...}
};
 
int main(){
   A* x = new B;
   delete x;
   return 0;
}

그런데 소멸자와 관련에서 C++ 프로그래밍 언어 책을 찾아본 적이 있는데, x가 가리키고 있는 객체의 타입은 정확히 무엇인지 모르므로 virtual을 선언하여 x가 가리키고 있는 객체를 완벽하게 소멸시킬 수 있다고 되어 있었습니다.
그렇기 때문에 맨 위와 같은 코드에서는 virtual 선언이 되어 있지 않으면 A 클래스의 소멸자만 실행되는 것으로 알고 있었습니다.

따라서 바로 위와 같은 코드(소멸자가 필요 없는 경우)에서도 기반 클래스인 A의 소멸자에 virtual이 반드시 들어가야 한다고 생각을 하는데, 책에서는 그렇게 되어 있지 않습니다.

그래서 궁금증이 생겼는데, 바로 위와 같은 코드에서는 virtual 선언이 필요가 없나요? 그리고 제가 생각하고 있는 것이 잘못 되었나요?

klyx의 이미지

필요 없습니다.
부모클래스의 가상함수는, 자식클래스에서 virtual 키워드를 쓰던 안쓰던, 오버라이딩을 하던 안하던 자동으로 가상함수가 됩니다.
소멸자도 마찬가지입니다.
따라서 생각하고 계신것은 잘못되었습니다.
명시적으로 쓰는게 좋다 나쁘다의 문제는 남을수 있겠지만요.

rexos33의 이미지

위의 클래스 정의로 아래 코드를 실행하면, 클래스 B의 객체가 소멸되더라도 기반(=베이스) 클래스인 A의 소멸자만 호출하게 됩니다.
따라서 클래스 B의 생성자에서 메모리 할당한 b의 메모리도 제거되어야 합니다. 기반 클래스인 A의 소멸자에 가상화가 이뤄지지 않으면 메모리 leak이 발생합니다.

int main(int argc, char* argv[])
{
    A* a = new B();
    delete a;
 
    return 0;
}

그러나 아래 처럼 하면, delete로 객체를 소멸시키면 객체 B의 소멸자, 그리고 객체 A의 소멸자가 호출되게 됩니다.

class A {
public:
    A() {}
    virtual ~A() {}
};
 
class B : public A {
public:
    B() {}
    ~B() {}
};
 
class C : public B {
public:
    C() {}
    ~C() {}
};

참고로 아래처럼 여러번 상속한 클래스 C 객체를 생성하였다가 소멸시켜도 ~C(), ~B(), ~A() 순서로 소멸자가 호출됩니다.
즉, 기반 클래스에서 한번만 가상함수로 선언하면 됩니다. (자동으로 상속됨)

int main(int argc, char* argv[])
{
    A* c = new C();
    delete c;
 
    return 0;
}

모두들 행복하세요~

bluekyu의 이미지

괜히 코드를 축약해서 표현했나봅니다.

class A{
    char a;
public:
    A(){a = new char[10];}
    ~A(){delete []a;}
};
 
class B: A{ // B 클래스 자체로는 소멸자를 따로 생성할 필요가 없음
    ...
public:
    B(){...}
};
 
int main(){
   A* x = new B;
   delete x;
   return 0;
}

질문의 요지는 위 코드에서 클래스 A의 소멸자에 virtual의 선언을 해주어야 하느냐 입니다.

제 생각은 A 클래스에 virtual 선언을 하지 않게 되면 B 객체의 소멸자가 호출되지 못해서 B 객체 부분의 메모리가 해제 되지 못한다고 생각합니다.
(즉, 질문에 써 있듯이 x가 가리키고 있는 객체의 정확한 타입을 알지 못하기 때문에 객체 A 부분의 메모리만 해제하고, 클래스 B의 소멸자를 호출하지 못해서 객체 B 부분의 메모리를 해제하지 못한다고 생각하는 것입니다.)

/*** Signature ******************
* blog: http://blog.bluekyu.me/ *
********************************/

neocoin의 이미지

뭐, 스펙과 교과서 그대로이지만, 이 예제를 실행해보시면 도움되겠네요.

아래에서 ~A 앞에 virtual 주고, 결과를 보고, 빼고 결과를 보세요. 원하시는 결과와 의도를 눈으로 확인하실수 있을 꺼에요.

"B의 destructor 가 영원히 변하지 않는다"면, 추가하실 필요 없을 듯 합니다. 하지만 B* x = new B(); 이런식으로 쓰면 답없죠.

#include <iostream>
using namespace std;
 
class A{
    char* a;
public:
    A(){a = new char[10];}
   ~A(){delete []a;cout << "destruct A" << endl;}
};
 
class B: public A{
public:
    B(){}                                                                                               
    ~B(){cout << "destruct B" << endl;}
};
 
int main(){
   cout << "start" << endl;
   A* x = new B();
   delete x;
   return 0;
}

klyx의 이미지

부모클래스의 소멸자는 virtual로 해주어야합니다.
이유는 말씀하신대로입니다.

익명 사용자의 이미지

B 클래스에는 해제해야할 자원(포인터)이 없으니 굳이 기반 클래스의 소멸자를 가상으로 선언할 필요는 없을 것으로 보입니다.

bluekyu의 이미지

답글 모두 감사합니다.

/*** Signature ******************
* blog: http://blog.bluekyu.me/ *
********************************/

emptynote의 이미지

저두 덕분에 공부에 큰 도움을 받았습니다.

질문 하신 분도, 답을 해주신 모든 분들 복받으실거에요.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.