2009-11-20 4 views
5

Microsoft Visual Studio 2008에서 다음과 같은 경고 메시지를 표시합니다.불완전한 유형의 메모리 누수가 있습니까?

경고 C4150 : 불완전한 유형의 포인터 'GLCM :: Component'삭제. 소멸자가 호출되지 않습니다.

이것은 여러 위치에서 선언 된 유형을 전달하기 위해 Handles를 정의했기 때문에 가능합니다. 이제 Handle 클래스는 주어진 객체에서 소멸자를 호출하지 않을 것이라고 주장합니다.

VLD가 실행 중이며 누출이 없습니다. 이것은 문자 그대로이 객체의 소멸자를 호출하지 않습니까, 아니면 "객체의 소멸자를 호출 할 수 없습니다"라는 경고입니까?

내게 또 다른 메모리 누수 문제가 있습니다. 하하.

+3

"이 문자 그대로이 개체의 소멸자를 호출하지 않습니까, 아니면"개체의 소멸자를 경고하지 않을 수 있습니까? " - 소멸자에 print 문을 추가하여 약 15 초 안에이를 테스트 할 수 있습니다. –

+0

하하, 좋은 지적입니다. 감사. – Reggie

+0

또는 소멸자에 중단 점을 넣으십시오. – gdunbar

답변

6

Pimpl를 사용하는 경우 그것은 자주 발생, 그래서 내가 거기 솔루션에 초점을 맞출 것이다 :

class FooImpl; 

class Foo 
{ 
public: 
    // stuff 
private: 
    Pimpl<FooImpl> m_impl; 
}; 

여기서 문제는 소멸자를 선언하지 않는 한, 그것은 자동으로 컴파일러에 의해, 인라인으로 생성 될 것입니다 . 물론 컴파일러는 완전한 유형의 FooImpl을 모를 것입니다.

따라서 비어있는 경우에도 소멸자를 명시 적으로 정의하고 전체 유형이 FooImpl 인 곳에서 정의를 지정해야합니다. 나처럼 당신이 (건설에 대한 복사 및 할당) 꽤 현명하게 당신의 Pimpl 클래스를 정의하는 경우

// cpp file 
class FooImpl 
{ 
}; 

Foo::~Foo() {} // Empty, but now correctly generated 
       // because FooImpl complete at this point. 

또한, 다음, 그 또한 .cpp 파일에 정의해야합니다.

정말 번거롭지만 구현 세부 사항을 멋지게 캡슐화 했으므로 그만한 가치가 있다고 생각합니다.

0

p-implidiom을 올바르게 따르지 않는 것 같습니다.

간단한 예 : 삭제 사용하여 다른 클래스의 정의 이후 지금

// header 
struct Other; // declare to use a pointer to it 

struct Handle { 
    Handle(); 
    ~Handle(); 

    void f(); 

private: 
    Other* _obj; 
}; 

// implementation (.cpp) 
#include "header" 

struct Other { 
    void f(); 
}; 

Handle() : _obj(new Other()) {} 
~Handle() { delete _obj; } 

void Handle::f() { _obj->f(); } 

때문에, 종류가 완료됩니다. 완전한 타입이 없으면, 컴파일러는 그것을 적절하게 파기하는 방법을 알 수 없다. (예는 dtor은 비공개 될 수도 가상 또는 가상이 아닌, 또는 수 있습니다.) C++ 표준 당

+0

나는 핸들 클래스 템플릿을 사용하고 있습니다. 그래서 그 핸들에 대해 전달 된 타입을 선언해서는 안됩니다. – Reggie

+0

아, 중요한 정보는 질문에서 제외되었습니다. :) 다른 구석들 사이에서 템플리트 인스턴스화와 전문화의 차이점을 배우고 싶지 않다면 그 시점에서 완료 될 유형이 필요합니다 (그리고 p-impl을 수행하지 않을 것임). –

+0

p-impl을 원하십니까? 그렇다면 템플릿 대신 템플릿이 아닌 템플릿을 사용하십시오. 그렇지 않고 완전한 유형 (예 : 기타의 클래스 정의)을 제공 할 수 없다면 Handle이 어떤 역할을합니까? –

6

(ISO/IEC 14882 : 2003 5.3.5/5) :

하는 경우 삭제되는 객체는 삭제 시점에서 불완전한 클래스 유형을 가지며 완전한 클래스는 중요하지 않은 소멸자 또는 할당 해제 함수를 가지므로 동작은 정의되지 않습니다.

따라서 클래스에 의미없는 소멸자가있는 경우 Visual C++에서이 상황을 처리하는 방법에 관계없이이 작업을 수행하지 마십시오.

class A; 

void func(A *p) 
{ 
     delete p; 
} 

int main(int argc, char **argv) 
{ 
     return 0; 
} 

경고하지만 오류 :

g에서
1

는 ++, 경고는 다음 코드를 사용하여도 재현 할 수

test.cpp: In function void func(A*): 
test.cpp:6: warning: possible problem detected in invocation 
    of delete operator: 
test.cpp:4: warning: A has incomplete type 
test.cpp:2: warning: forward declaration of struct A 
test.cpp:6: note: neither the destructor nor the class-specific 
    operator delete will be called, even if they are declared 
    when the class is defined. 

g는 ++ 매우 여기에 분명히 그 소멸자 때문에 호출되지 않습니다 상태 동안 소멸자가 필요한지 아닌지 또는 소멸자가 어디에 있는지를 알지 못합니다.

이 경우 delete()는 C 호출 free()로 다운 그레이드합니다. 즉, 객체 메모리 자체를 비울 뿐이지 만 내부적으로 (예 : 생성자에서) 객체 자체가 할당 한 힙 데이터는 유지되며, 자유 (p)와 같은 행동.

MS VC/++의 경우 비슷한 방법이라고 생각합니다. 올바른 방법은 A의 인터페이스 헤더를 포함하는 것입니다.

1

삭제할 개체에 힙 할당 개체에 대한 포인터가 없으면 소멸자가 호출되지 않아도 누수가 발생하지 않을 수 있습니다. 그러나 이것을 시도하지 마십시오. James McNellis가 his answer에서 언급했듯이 C++ 표준에 따라 정의되지 않은 동작입니다.

이 명시 적으로 또는 스마트 포인터에서 삭제되면 먼저 개체 소멸자가 실행되고 메모리가 할당 해제됩니다.VC++는 기본적으로 어떤 소멸자가 실행될 지 모르기 때문에 아무 것도 실행하지 않고 대신 메모리를 할당 해제한다는 사실을 알려줍니다. 객체가 POD 유형이거나 힙 할당 객체 (포인터가 포함되어 있지 않거나 포인터가 null 또는 다른 객체 소유의 객체를 포함하므로 할당을 해제 할 필요가 없음)로 설정된 포인터가없는 경우 누출이 발생할 이유가 없습니다.

아마도 VC++는 먼저 객체를 void*으로 캐스팅했을 때와 동일한 동작을 생성합니다. 그러나 다시 이것에 의지하지 마십시오. 당신이 VC++ 컴파일러의 버전을 변경하거나 패치하는 순간 코드가 깨지기 쉽습니다.

관련 문제