2016년 3월 13일 일요일

[c++] virtual 소멸자

곧바로 예제부터 보자.
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class AAA  
  5. {  
  6.     char* str1;  
  7. public:  
  8.     AAA(char* _str1){  
  9.         str1= new char[strlen(_str1)+1];  
  10.         strcpy(str1, _str1);  
  11.     }  
  12.     ~AAA(){         // virtual ~AAA()  
  13.         cout<<"~AAA() call!"<<endl;  
  14.         delete []str1;  
  15.     }  
  16.     virtual void ShowString(){  
  17.         cout<<str1<<' ';  
  18.     }  
  19. };  
  20.   
  21. class BBB : public AAA  
  22. {  
  23.     char* str2;  
  24. public:  
  25.     BBB(char* _str1, char* _str2) : AAA(_str1){  
  26.         str2= new char[strlen(_str2)+1];  
  27.         strcpy(str2, _str2);  
  28.     }  
  29.     ~BBB(){  
  30.         cout<<"~BBB() call!"<<endl;  
  31.         delete []str2;  
  32.     }  
  33.     virtual void ShowString(){  
  34.         AAA::ShowString();  
  35.         cout<<str2<<endl;  
  36.     }  
  37. };  
  38.   
  39. int main()  
  40. {  
  41.     AAA * a=new BBB("Good""evening");  
  42.     BBB * b=new BBB("Good""morning");  
  43.   
  44.     a->ShowString();   
  45.     b->ShowString();  
  46.   
  47.     cout<<"-----객체 소멸 직전----"<<endl;  
  48.     delete a;  
  49.     delete b;  
  50.   
  51.     return 0;  
  52. }  
 위 코드를 보면, AAA클래스에서 생성자에서 동적 할당 하기에 소멸자에서 메모리 해제 하고 있고 마찬가지로 BBB클래스의 생성자에서 동적 할당을 하고 있어서 소멸자에서 메모리 해제 하고 있는 형태를 가지고 있다. BBB클래스의 객체가 소멸될때, AAA클래스의 소멸자도 호출이 된다. 
  BBB클래스 객체가 생성이 되면, AAA클래스의 생성자에 의해서도 메모리 공간 동적 할당 할것이고, BBB클래스도 마찬가지이다. 이 두곳에서 할당된 메모리 공간이 적절히 해제 될것이기에 우리가 신경 쓰지 않아도 되지만, 여전히 문제가 존재 한다. 
  1. int main()  
  2. {  
  3.     //AAA * a=new BBB("Good", "evening");  
  4.     BBB * b=new BBB("Good""morning");  
  5.   
  6.     a->ShowString();   
  7.     b->ShowString();  
  8.   
  9.     cout<<"-----객체 소멸 직전----"<<endl;  
  10.     //delete a;  
  11.     delete b;  
  12.   
  13.     return 0;  
  14. }  
 위의 코드는 정상적인 코드 이다. BBB클래스의 객체가 생성되는 과정에서 AAA클래스의 생성자도 호출되므로, 소멸될때는 AAA,BBB클래스의 소멸자가 아래와 같이 다 호출이 되는것을 알 수 있다.

그럼 다음 코드를 보자.
  1. int main()  
  2. {  
  3.     AAA * b=new BBB("Good""morning");  
  4.     b->ShowString();  
  5.   
  6.     cout<<"-----객체 소멸 직전----"<<endl;  
  7.   
  8.     delete b;  
  9.   
  10.     return 0;  
  11. }  
 위의 경우를 생각해보자. BBB클래스는 AAA클래스를 상속하기 때문에 선언부가 3번째 줄처럼 바뀔수도 있을 것이다. 하지만 실행해보면, AAA클래스의 소멸자만 호출 되고 있음을 알 수 있다. 바로 이 부분에서 메모리의 유출이 발생된다. 

 AAA클래스의 포인터로 참조 하지만, 생성되는 객체는 B클래스의 객체이기 때문에, AAA,BBB 클래스에서도 생성자부분에서 메모리 공간이 동적 할당되는데는 문제가 없다. 하지만 객체가 소멸될때 A클래스 내에서 동적 할당한 메모리 공간은 반환되지만, BBB는 반환되지 않았던 것이다. 객체가 소멸하고자 했을때 소멸의 주체가 되는 포인터가 AAA클래스의 포인터였기 때문에 이런 일이 발생한다. 

그럼 위 문제를 어떻게 해결 할 것인가? 바로 Virtual 소멸자(virtual destructor)을 써줌으로써 간단하게 해결이 된다. 아래와 같이 간단하게 virtual 키워드만 소멸자 앞에 붙여주면 되는 것이다. 
  1. class AAA  
  2. {  
  3.       
  4. public:  
  5.       
  6.     virtual ~AAA(){         // virtual만 붙여주면 된다.  
  7.         cout<<"~AAA() call!"<<endl;  
  8.         delete []str1;  
  9.       
  10. };  
 virtual 소멸자의 경우 AAA클래스를 상속하고 있는 BBB클래스의 소멸자를 호출하게 된다. 이어서 BBB클래스가 AAA클래스를 상속하고 있으므로 AAA클래스의 소멸자를 호출하게 됨으로써 소멸자들이 정상적으로 호출되는 결과를 볼수 있다. 

댓글 없음:

댓글 쓰기

[Effective C++] 항목 30 : 인라인 함수는 미주알고주알 따져서 이해해 두자.

인라인 함수를 사용하면 컴파일러가 함수 본문에 대해 문맥별 최적화를 걸기가 용이해집니다. 인라인 함수의 아이디어는  함수 호출문을 그 함수의 본문으로 바꿔치기하자는 것  남발했다가는 코드의 크기가 커질 게 뻔하다. 인라인 함수로 부풀려진 ...