2014-03-31 4 views
1

이 코드로 인해 메모리 누수가 발생하는 이유는 무엇입니까? 기본 클래스 소멸자가 "가상"으로 만들어지면 메모리 누수 문제가 해결 될 것이라고합니다. 왜?파생 클래스에 대한 포인터로 인한 메모리 누수가 발생했습니다.

class base { 
public: 
    base() { ptr = new int[1024]; } 
    ~base() { delete [] ptr; } 
private: 
    double *ptr; 
}; 
class der : public base { 
public: 
    der() { name = new char[1024]; } 
    ~der() { delete [] name; } 
private: 
    char *name; 
}; 
int main() { 
    base* aPointer = new derived; 
    delete aPointer; 
    return 0; 
} 
+2

http://stackoverflow.com/questions/461203/when-to-use-virtual-destructors?lq=1 – chris

+1

"가상 소멸자"에 대해서만 google을 사용하면 이유에 대한 많은 정보를 찾을 수 있습니다. 그 뒤에. 방대한 양의 정보를 사용할 수 있어야한다는 것이 C++의 근본적인 문제입니다. –

답변

1

누수가 있으며 정의되지 않은 동작입니다.

파생 클래스가 힙 할당 변수를 추가로 도입하지 않았다면 아직 정의되지 않은 동작 일 수는 있지만 추측은 무의미합니다. base 가상 소멸자, 추가적인 파생 클래스와 그 회원들과 다른 기지에 대한 소멸자를 호출 할 실패합니다 base*를 사용하여 파생 개체의 삭제를하지 않는

는 ... 여기 name는 해제되지 않습니다.

기본 클래스 소멸자가 "가상"으로 만들어지면 메모리 누수 문제가 해결된다고합니다. 왜?

virtual 기능 컴파일러는 오직 기본 클래스에 대한 포인터 또는 레퍼런스를 갖는 코드로부터 유도 된 클래스 - 특정 함수 호출을 조정하는 방법이다. 이것은 C++의 객체 지향 프로그래밍 지원의 기본적인 측면입니다. 이해할 수없는 경우 좋은 책이나 온라인 자습서를 받아야하지만 매우 간결한 소개를해야합니다. 가상 함수는 함수 포인터가있는 것처럼 작동합니다 일반적으로 기본 클래스의 함수 구현을 가리키는 기본 클래스에 있지만 파생 클래스 생성자가 실행되면 함수 포인터는 자체 구현의 주소로 덮어 씁니다 (실제로 가상 디스패치는 클래스에 대한 함수 포인터의 테이블을 사용하는 경향이 있습니다. 더 나은 메모리 효율성, 그러나 관찰 할 수있는 기능은 내가 설명한 것과 비슷합니다). 따라서 deletebase*의 정적 유형을 가진 포인터를 사용하면 유도 된 소멸자는 실행되지 않습니다.

0

개체를 삭제할 때 C++에서 호출 할 소멸자 함수를 결정하는 두 가지 방법이 있습니다.

해당 개체에 대한 포인터가 T*이지만 T::~T()이 가상 함수 인 것으로 선언 된 경우 C++은 가상 함수 테이블을 검색하여 호출 할 소멸자를 찾습니다. 가상 함수 테이블은 포인터의 유형이 아니라 실제로 생성 된 클래스에 의해 결정되는 객체의 해당 인스턴스 메모리에 저장된 포인터를 통해 찾습니다. base* aPointer = new der에 의해 초기화 된 객체의 경우 ~base()이 가상으로 선언 된 경우 C++은 실제 구체적인 클래스 (der이 아닌 base이 아님)의 가상 함수 테이블을 찾을 수 있으며, 그렇게했을 때 소멸자 함수 ~der()을 찾습니다. . 그러면 name이 삭제 된 다음 der에서 파생 된 클래스에 의해 정의 된 소멸자가 호출됩니다. 즉 ~base()이 호출되어 ptr이 삭제됩니다. base이 다른 클래스에서 파생 된 경우 해당 소멸자는 해당 클래스의 소멸자를 호출하고 아무 것도 파생되지 않은 클래스에 도달 할 때까지 계속 호출합니다.

그러나 개체에 대한 포인터가 타입 T*으로 선언하고, T의 소멸자가 가상하지 않은 경우, 다음 컴파일러가 사용할 수있는 유일한 소멸자 당신이 즉, T 정의 할 때 정의 된 하나입니다

, T::~T().따라서 aPointerbase*으로 선언되었으므로 delete aPointer은 먼저 ~base()을 호출하고 ptr을 삭제하고 소멸자가 호출 될 수있는 추가 기본 클래스가 없기 때문에 반환합니다. 이 경우 소멸자 ~der()이 호출되지 않으므로 name은 삭제되지 않습니다. 그것은 메모리 누수입니다.

Base * base = new Derived(); 
... 
delete base; 

일반적으로, 무슨 일하는 유일한 Base::~Base()는 의미라고 할 것입니다 :

2

두 클래스 BaseDerived은 소멸자가 당신이 쓰는 그렇게 할 때, VTABLE에없는 의미, 가상 소멸자가 없습니다 Derived::~Derived()이 호출되지 않으므로 Derived::~Derived()이 호출 될 것으로 예상되면 메모리 누수가 발생합니다.

이 문제의 해결책은 vtable에 넣을 수 있도록 소멸자 가상을 만드는 것입니다. 따라서 "delete base;"은 동적 유형 (Derived)의 소멸자를 호출하고 객체를 올바르게 파괴합니다.