1

According to the standard, polymorphism with a missing virtual destructor leads to undefined behavior. In practice, it really leads to the destructor for the derived class not being called when the parent class is deleted. However, does it also lead to memory leaks in any common compilers/systems? I'm particularly interested in g++ on Android/Linux.

Specifically, I'm referring to whether the deletion of memory for the derived class will somehow leak. Consider:

class Base {}
class Derived {
    int x;
}

If I delete a Base* to a Derived, will I leak 4 bytes? Or does the memory allocator already know how many bytes to free based on the allocation?

IanPudney
  • 5,941
  • 1
  • 24
  • 39
  • 1
    "polymorphism with a missing virtual destructor leads to undefined behavior." It doesn't have to. It is only an issue if you call `delete` on a derived object from a base pointer. – juanchopanza Jul 27 '15 at 22:07
  • @CamelToe no I don't, but there is some preexisting code and I don't want to fix it unless there is actually a problem. Also, I'm more curious than anything. – IanPudney Jul 27 '15 at 22:15

3 Answers3

3

It certainly can do. Consider:

class A
{
public:
 virtual void func() {}
};

class B : public A
{
public:
   void func() { s = "Some Long String xxxxxx"; }
private:
   std::string s;
   // destructor of B will call `std::string` destructor.
};

A* func(bool b)
{
   if (b)
       return new B;
   return new A;
}

... 
   A* a = func(true);
...
   delete a;

Now, this will create a memory leak, as std::string s in the B object is not freed by A::~A - you need to call B::~B, which will only happen if the destructor is virtual.

Note that this applies to ALL compilers and all runtime systems that I'm aware of (which is all the common ones and some not so common ones).

Edit:

Based on the updated actual question: Memory de-allocation happens based on the allocated size, so if you can GUARANTEE that there NEVER is a single allocation happening because of the construction/use of the class, then it's safe to not have a virtual destructor. However, this leads to interesting issues if a "customer" of the base-class can make his/her own extension classes. Marking derived classes as final will protect against them being further derived, but if the base class is visible in a header-file that others can include, then you run the risk of someone deriving their own class from Base that does something that allocates.

So, in other words, in something like a PImpl, where the Impl class is hidden inside a source file that nobody else derives from, it's plausible to have this. For most other cases, probably a bad idea.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
1

A missing destructor will cause undefined behavior specifically because it's implausible for the compiler to know exactly what the side effects might be.

Think of it as the cleanup side of RAII. In that case, if you manage to not clean up despite claiming that you did, side effects might be:

  • Leaked memory (you allocated something... when do you deallocate it now?)
  • Deadlocks (you locked something... when do you unlock it now?)
  • Sockets remaining open (you opened it sometime... but now when do you close it?)
  • Files remaining open (you opened it sometime... but now when do you flush it?)
  • Accessing invalid pointers (for example, you updated a pointer to some member... but now when do you unset it?)
  • Your hard drive gets erased (technically this is a valid answer for any undefined behavior)
inetknght
  • 4,300
  • 1
  • 26
  • 52
  • Believe me, I am well aware of the meaning of "undefined behavior", but I'm looking for the *practical* behavior on a particular system. – IanPudney Jul 27 '15 at 22:14
  • The *practical* behavior is that it is *not worth the trouble* to try to optimize away a given destructor. I cannot stress that enough. I know of no modern hardware, mobile or otherwise, where the trouble of fighting to get around the standard is worth the gains you might get. If you're *that* limited then I suggest you look at pure C instead. – inetknght Jul 27 '15 at 22:55
1

This should cause Undefined Behaviour which means it might also cause memory leaks. In 5.3.5/3 (n4296 c++14) for delete you have:

In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.

marcinj
  • 48,511
  • 9
  • 79
  • 100