티스토리 뷰

기본 클래스를 생성할 때 파괴자를 가상 파괴자로 선언해 두는 습관이 좋다고 합니다.


예제를 통해 그 이유를 살펴봅시다.


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
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
 
class Parent
{
    char *m_ch;
public:
    Parent(const char * ch = "")
    {
        cout << "Parent(const char * ch) is called" << endl;
        m_ch = new char[strlen(ch) + 1];
        strcpy(m_ch, ch);
    }
    ~Parent()
    {
        cout << "~Parent() is called" << endl;
        delete[] m_ch;
    }
    virtual void Print()
    {
        cout << "Parent::m_ch : " << (int *)m_ch << " : " << m_ch << endl;
    }
};
 
 
class Child :public Parent
{
    char *m_chc;
public:
    Child(const char * ch) :Parent(ch) 
    {
        cout << "Child(const char * ch) is called" << endl;
        m_chc = new char[strlen(ch) + 1];
        strcpy(m_chc, ch);
    }
    ~Child() 
    {
        cout << "~Child() is called" << endl;
        delete[] m_chc;
    }
    void Print()
    {
        Parent::Print();
        cout << "Child::m_chc : " << (int *)m_chc << " : " << m_chc << endl;
    }
};
 
 
 
 
int main()
{
    {
        Parent *= new Child("Test");
        p->Print();
        delete p;
    }
    cout << "Program end" << endl;
    return 0;
}
 
 
cs



프로그램은 간단합니다. Parent 클래스는 생성자를 통해 문자열을 입력받아 문자열의 길이만큼 char형 동적배열을 생성하고 이 주소를 멤버 변수에 저장합니다. Child 클래스는 Parent 클래스를 public 상속합니다. Child 클래스는 Parent와 마찬가지로 생성자를 통해 문자열을 입력받아 문자열의 길이만큼 동적배열을 생성하고 멤버변수에 주소를 저장해둡니다. 그리고 Child 클래스의 생성자는 Parent 생성자를 호출합니다.


따라서 56번째 줄을 실행하면 Child 클래스의 동적 객체가 생성되고 Parent 포인터 변수 p에는 이 동적 객체의 주소가 담깁니다.
이 동적객체 안에는 Child::m_chc 와 Parent::m_ch 에 "Test"라는 문자열을 담은 동적배열의 주소가 저장된 객체가 생성됩니다.
57번째 줄에는 출력을 하고 58번째 줄에서는 이 동적 객체를 파괴합니다.

얼핏보아서는 잘 동작하는 프로그램처럼 보입니다만 실행결과는 좀 이상한 결과를 낳습니다.


결과를 보면 Child 클래스의 부모 클래스인 Parent 클래스의 파괴자만 실행되고 Child 클래스의 파괴자는 실행되지 않은 것을 확인할 수 있습니다. 프로그램상에서 이런일이 계속 발생할 경우 메모리가 반환이 안되는 문제가 생기게 되겠지요. 


위의 예제에서 보듯 기본 클래스가 가상 파괴자를 쓰지 않을 경우 포인터형(Parent *)에 해당하는 파괴자(~Parent())만 호출하게 되어 자식 클래스의 파괴자는 실행이 되지 않습니다.

이 문제는 기본 클래스의 파괴자를 가상함수로 선언함으로써 해결할 수 있습니다. 

위의 소스코드에서 파괴자 기본 클래스의 파괴자 ~Parent() 에 virtual 키워드를 넣고 다시 프로그램을 실행시켜 봅니다.


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
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
 
class Parent
{
    char *m_ch;
public:
    Parent(const char * ch = "")
    {
        cout << "Parent(const char * ch) is called" << endl;
        m_ch = new char[strlen(ch) + 1];
        strcpy(m_ch, ch);
    }
    virtual ~Parent()
    {
        cout << "~Parent() is called" << endl;
        delete[] m_ch;
    }
    virtual void Print()
    {
        cout << "Parent::m_ch : " << (int *)m_ch << " : " << m_ch << endl;
    }
};
 
 
class Child :public Parent
{
    char *m_chc;
public:
    Child(const char * ch) :Parent(ch) 
    {
        cout << "Child(const char * ch) is called" << endl;
        m_chc = new char[strlen(ch) + 1];
        strcpy(m_chc, ch);
    }
    ~Child() 
    {
        cout << "~Child() is called" << endl;
        delete[] m_chc;
    }
    void Print()
    {
        Parent::Print();
        cout << "Child::m_chc : " << (int *)m_chc << " : " << m_chc << endl;
    }
};
 
 
 
 
int main()
{
    {
        Parent *= new Child("Test");
        p->Print();
        delete p;
    }
    cout << "Program end" << endl;
    return 0;
}
 
 
cs





기본 클래스의 파괴자를 가상 함수로 변경하니 ~Child()가 잘 동작하는 것을 확인할 수 있습니다.


기본 클래스를 확장할 계획이 있다면 되도록 가상 파괴자를 쓰는 것이 좋겠지요. :)





'프로그래밍(programming) > C++' 카테고리의 다른 글

c++11 : auto 키워드  (0) 2016.03.12
c++ 기초플러스  (0) 2016.03.04
다중 상속  (0) 2013.10.17
순수 가상함수(pure virtual function)와 추상클래스  (0) 2013.10.16
virtual 키워드 2  (0) 2013.10.16
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함