Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incomplete Type memory leaks?

Microsoft Visual Studio 2008 is giving me the following warning:

warning C4150: deletion of pointer to incomplete type 'GLCM::Component'; no destructor called

This is probably because I have defined Handles to forward declared types in several places, so now the Handle class is claiming it won't call the destructor on the given object.

I have VLD running and I'm not seeing any leaks. Is this literally not calling the destructor for this object or is this a "may not call destructor for object" warning?

Yet another memory leak question from me, haha.

like image 306
Reggie Avatar asked Nov 20 '09 00:11

Reggie


4 Answers

Per the C++ Standard (ISO/IEC 14882:2003 5.3.5/5):

If the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.

So, if your class has a non-trivial destructor, don't do this, regardless of how Visual C++ handles this situation.

like image 73
James McNellis Avatar answered Nov 16 '22 14:11

James McNellis


It often happen when using Pimpl, so I'll focus on the solution there:

class FooImpl;

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

The problem here is that unless you declare a destructor, it will be automatically generated, inline, by the compiler. But of course, the compiler will have no idea of the complete type of FooImpl there.

You thus have to explicitly define the destructor, even if empty, and put the definition somewhere where the complete type of FooImpl is visible.

// cpp file
class FooImpl
{
};

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

Also, if like me you defined your Pimpl class to be pretty smart (regarding construction, copy and assignment), then those will also need to be defined in the .cpp file.

It's really a hassle, but then you have nicely encapsulated your implementation details, so I suppose it's worth it.

like image 44
Matthieu M. Avatar answered Nov 16 '22 13:11

Matthieu M.


In g++, the warning could be reproduced too using following code:

class A;

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

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

Warnings but no any errors:

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.

While g++ states very clear here that destructor will not be called because it doesn't know whether needs a destructor or not, or where is the destructor.

In this case delete() downgrates to the C call free(), that is, it just free the object memory itself, but any heap data allocated by the object itself internally (e.g., in constructor) will be keeped, the same behavior as free(p).

I think it would be a similar way if in MS VC/++; and a correct way is you to include the interface headers of A.

like image 24
Test Avatar answered Nov 16 '22 14:11

Test


You may have no leaks even with the destructor not called if the object being deleted doesn't contain pointers to heap-allocated objects. However don't try this - as James McNellis mentions in his answer it's undefined behaviour according to the C++ standard.

When delete is called (explicitly or from a smart pointer) first the object destructor is run and then memory is deallocated. VC++ basically tells you that it has no idea of what destructor to run and so will not run any but instead will just deallocate memory. If the object is a POD type or it has no pointers set to heap-allocated objects (either contains no pointers or contains pointers set to null or to objects owned by someone else and therefore not needed to deallocate) there's no reason for a leak.

So perhaps VC++ will produce the same behaviour as it would with first casting the object to void*. But again don't rely on this - at best you will have unportable code, at worst your code will break apart as soon as you change version of or patch your VC++ compiler.

like image 1
sharptooth Avatar answered Nov 16 '22 14:11

sharptooth