Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is extending a base class with non-virtual destructor dangerous?

In the following code:

class A {
};
class B : public A {
};
class C : public A {
   int x;
};

int main (int argc, char** argv) {
   A* b = new B();
   A* c = new C();

   //in both cases, only ~A() is called, not ~B() or ~C()
   delete b; //is this ok?
   delete c; //does this line leak memory?

   return 0;
}

When calling delete on a class with a non-virtual destructor with member functions (like class C), can the memory allocator tell what the proper size of the object is? If not, is memory leaked?

Secondly, if the class has no member functions, and no explicit destructor behaviour (like class B), is everything ok?

I ask this because I wanted to create a class to extend std::string, (which I know is not recommended, but for the sake of the discussion just bear with it), and overload the +=, + operator. -Weffc++ gives me a warning because std::string has a non virtual destructor, but does it matter if the sub-class has no members and does not need to do anything in its destructor?

FYI the += overload was to do proper file path formatting, so the path class could be used like:

class path : public std::string {
    //... overload, +=, +
    //... add last_path_component, remove_path_component, ext, etc...
};

path foo = "/some/file/path";
foo = foo + "filename.txt";
std::string s = foo; //easy assignment to std::string
some_function_taking_std_string (foo); //easy implicit conversion
//and so on...

I just wanted to make sure someone doing this:

path* foo = new path();
std::string* bar = foo;
delete bar;

would not cause any problems with memory allocation?

like image 387
Akusete Avatar asked Apr 08 '10 02:04

Akusete


1 Answers

No, it's not safe to publically inherit from classes without virtual destructors, because if you delete the derived through a base you enter undefined behavior. The definition of the derived class is irrelevant (data members or not, etc.):

§5.3.5/3: In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined. (Emphasis mine.)

Both of those examples in your code lead to undefined behavior. You can inherit non-publicly, but that obviously defeats the purpose of using that class then extending it. (Since it's not longer possible to delete it through a base pointer.)

This is (one reason*) why you shouldn't inherit from standard library classes. The best solution is to extend it with free-functions. In fact, even if you could you should prefer free-functions anyway.


*Another being: Do you really want to replace all your string usage with a new string class, just to get some functionality? That's a lot of unnecessary work.

like image 55
GManNickG Avatar answered Sep 20 '22 08:09

GManNickG