Let's say that I use an external library full of various classes. When I can safely inherit from one of those classes? I know that the base class must have a virtual destructor. Is there something else I should check before using the class as the base class? Can I be sure that it is safe only if the docs state so?
If the documentation states that it is safe to derive a type, follow the documentation. If, for some reason, it behaves in a way that goes against the documentation, then this is a problem with the library and is a bug for the authors to fix or offer workarounds for, because they are not committing to the API that they guarantee in the documentation.
Any type that is not final can be derived "safely"; what matters more is how this type is handled and destroyed. If you inherit from a type that has no virtual destructor, this doesn't inherently break anything; it just prevents having the derived type's destructor get called if you destroy that from a handle to the base.
If you only ever destroy the type from a handle to the derived type, (e.g. you either hold it concretely, or never destroy it from a handle to the base), then this has no consequence.
To better explain my point, imagine the following hierarchy:
class Base {
public:
// No virtual destructor
...
};
class Derived : public Base {
public:
...
private:
std::string m_something; // some leakable object
};
The derivation of Derived from Base is completely safe to do. What matters is how it gets destroyed for whether there will be a problem. For this there are two different cases to consider: Automatic and Dynamic cases.
Automatic Objects
Automatic types ("by-value" types) are safe, regardless of whether they have static lifetime or not
auto d = Derived{ ... };
static auto sd = Derived{ ... };
At the end of their lifetime, the destructor Derived::~Derived will be called, since the type is concretely known
Dynamic Objects
Dynamic objects don't get destroyed on their own. Their resources need to be cleaned up eventually, either automatically with RAII in a smart pointer, by someone calling delete, or by someone explicitly calling ~T() and freeing the memory.
These are still safe if they are destroyed by a handle to the derived type, but will not be if they are destroyed by a handle to the base.
auto* d1 = new Derived{ ... };
auto* d2 = new Derived{ ... };
// Deleting as a pointer to Base; ~Derived won't be called because ~Base is virtual
// This would be a memory leak
delete static_cast<Base*>(d1); // bad
// Deleting as a pointer to Derived -- ~Derived will be called, this is fine
delete d2; // good
In terms of Smart Pointer types:
Shared Pointer
shared_ptr types are safe, since they always destroy objects from the concrete type -- even if they get aliased to a base class.
void accept_base(std::shared_ptr<Base> b);
auto d = std::make_shared<Derived>(...);
// still safe
accept_base(std::move(d));
Unique Pointer
unique_ptr types are not safe by default due to the default deleter deleting based on the T type of unique_ptr.
For example:
auto d = std::make_unique<Derived>(...);
auto b = std::unique_ptr<Base>{std::move(d)};
// b will be destroyed at end of scope by calling ~Base, which is not virtual!
Even with all of this said: If you're using a library that explicitly states that you are meant to derive some XYZ class, then you should still assume that this is how the class should be used. At that point, if something undesirable occurs, it will be up to the library maintainer to ensure that their code performs as documented, since it's part of their expressly stated API.
If you intend to call methods from a Base class reference or pointer, you should also check if they are declared virtual.
Apart from that I would look into the documentation of the class and whether it is declared final
You actually do not need a virtual destructor for it to be safe to inherit from a class. You only need a virtual destructor, if you want to use (and thereby destroy) the class from a pointer to its base class.
It all depends on how you intend to use the derived class.
For instance, if you just want to make a class that inherits from the given class, but you do not intend to use it in a base class pointer or reference.
Base baseObj;
Derived derivedObject; // This does not create any problems
If you want to use it from a pointer or reference (this also applies to smart pointers of course) to the base class like this:
Base* basePtr = new Base();
Base* basePtrToDerived = new Derived();
Derived* derivedPtrToDerived = new Derived();
// Do stuff here
delete basePtr;
delete basePtrToDerived; // if Base has no virtual destructor, only the destructor of Base is called
delete derivedPtrToDerived; // This will always call the destructor of Derived
you need a virtual destructor.
Related
Consider these two classes in C++11:
class A
{
ArbitraryClass someInstance;
};
class B : public A
{
};
And consider I use B as such:
B *foo = new B;
delete foo;
As I understand, the implicit destructor of A will not be called here.
Is someInstance still destroyed under these circumstances, because it becomes "associated" as a member of B? Or do I need to declare a virtual destructor on A to automatically trigger the member's destruction?
You need a virtual destructor in a delete expression's statically known class (in your case B) if that class is different from the most derived class of the object (in your case also B). In your case those classes are the same, so you don't need a virtual destructor: everything's destroyed properly. But if you had made the pointer type A*, then you'd need a virtual destructor in A to avoid Undefined Behavior (nasal daemons and such).
How do I ensure ...
Mostly by doing nothing.
As I understand, the implicit destructor of A will not be called here.
Wrong.
Is someInstance still destroyed under these circumstances
Yes.
because it becomes "associated" as a member of B?
Because it is a member of A, and A is destroyed.
Or do I need to declare a virtual destructor on A to automatically trigger the member's destruction?
Not in the case of the code you posted, but if (as is likely) you will be using polymorphism by deleting pointers to A that could be B or other subclasses of A you should give A a virtual destructor.
In terms of inheritance, I understand that it's advised for your classes' destructors to be virtual, so the base class's destructor gets called correctly in addition to any derived destructors. However, I'm wondering if there are any stack-related issues that relate to derived objects in the following scenario.
Let's suppose we have a Base class that doesn't have a destructor (for whatever reason):
class Base{};
and a Derived class that DOES have a destructor:
class Derived : public Base
{
~Derived(){}
};
And in the main...:
int main()
{
Derived a;
return 0;
}
Do I run into any issues from the Base class not having a destructor? My initial guess is that the compiler will just generate a default destructor for the Base class. Again, my question is mostly related to the stack rather than dynamic memory: is there any weirdo scenario I need to look out for in order to avoid a Derived destructor being called and the Base destructor is not?
The rule you're thinking of is that if you delete an object of a derived type through a pointer to one of its base types and that base type does not have a virtual destructor the behavior is undefined. The code here doesn't delete anything, so the rule does not apply.
To ensure safety, it is sufficient that every destructor (implicit or explicit) be at least one of:
virtual (for base classes if you need to delete subclass instances through base class pointers)
protected (to ensure that it is impossible to attempt to delete through a base class pointer)
final (actually an attribute of the class, to avoid the entire possibility of subclasses).
There are a few rare edge cases where it is possible to safely call destructors, but they are generally a sign of bad design and are easy to avoid if you manage to happen across one of them.
As an aside, note that std::shared_ptr type-erases its deleter, so std::shared_ptr<Base> will work even if Base does not have a public destructor.
Your base class has an implicit destructor. All will be fine.
A virtual base class destructor is used to allow a derived constructor to run when destructing via a pointer or reference to the base class. So in your case, this would be unsafe:
void destruct(Base &b) { b.~Base(); }
Derived d; destruct(d);
But this will be perfectly safe:
void destruct(Derived &d) { d.~Derived(); }
Derived d; destruct(d);
Looking at the C++ language standard, is there any way to call only derived class destructor, without calling destructor of the base class?
So, for classes
class Base { public: virtual ~Base() {} };
class Derived : public Base { public: ~Derived();};
if would be possible to write code like
Base *basePtr = new Derived();
//do something with basePtr
// Now somehow destroy Derived while keeping Base - call ~Derived() only,
// line below however will call both ~Derived() and ~Base() - how it can be done?
dynamic_cast<Derived*>(basePtr)->~Derived();
So, after execution of the code above basePtr will point to Base object only, like if it was created by
Base *basePtr = new Base();
plus any modifications to the Base object caused by manipulating basePtr between calling new Derived() and destroying Derived class?
Or, is this forbidden and it is impossible to do?
No, this is not possible. The standard demands that the destruction of a Derived object destroys the whole object, including the Base subobject. Anything else would not be a destruction according to C++'s understanding of object lifetime.
Depending on what you want to achieve, consider to copy the Base out of the derived first
std::unique_ptr<Base> basePtr(new Derived());
//do something with basePtr
basePtr.swap(std::unique_ptr<Base> (new Base(*basePtr))); //splice the Base part out of the derived object
//basePtr now points to the spliced Base object.
Another approach would be to hold the additional members that derived has in a boost::optional (or just a pimpl) and reset that to get a "stripped" Derived object that still has its Base class part. This will however not affect virtual function dispatch.
As you have formulated the question, it is not possible to achieve what you are asking for. Unless you have a memory leak, the only case when explicitly calling a destructor does not lead to undefined behavior is if the object was created by placement new. Even then, calling the destructor will automatically call the destructor of every member and base class.
This is as it should be. Otherwise it would be very difficult to write a correct container class or memory manager.
The standard says that the lifetime of an object ends as soon as it enters the destructor. It does not become a base class object. It ceases to be an object, entirely. Also, if this was not the case, what would be the status of a class deriving from multiple bases after such a trick?
Ultimately, the "need" for such a functionality is a sign of bad design. I would guess your use case more likely requires composition. See if you can't solve it with a new class which holds one instance of what is currently the base class and one optional, replaceable component of (a smart pointer to) some dummy class which serves as an interface to and the new common base of your current derived classes. That way you can remove (and destruct) those sub objects without touching the base.
Destructors are called automatically in the reverse order of construction. I do not believe there is any way around this.
This is not possible to do unless your derived class has nothing to do with your base class.
Destructors are called in automatic way.Explicitly call destructor may result in undefined behavior.
Since boost::/std::shared_ptr have the advantage of type-erasing their deleter, you can do nice things like
#include <memory>
typedef std::shared_ptr<void> gc_ptr;
int main(){
gc_ptr p1 = new int(42);
gc_ptr p2 = new float(3.14159);
gc_ptr p3 = new char('o');
}
And this will correctly delete all pointer thanks to the correct deleter being saved.
If you ensure that every implementation of your interface always gets created with shared_ptr<Interface> (or make_shared<Interface>), do you actually need a virtual destructor? I would declare it virtual anyways, but I just want to know, since shared_ptr will always delete the type it was initialized with (unless another custom deleter is given).
I would still follow the common rule for classes that are meant to be derived:
Provide either a public virtual destructor or a protected non-virtual destructor
The reason is that you cannot control all of the uses, and that simple rule means that the compiler will flag if you try to delete through the wrong level in the hierarchy. Consider that shared_ptr does not guarantee that it will call the appropriate destructor, only that it will call the destructor of the static type that was used as argument:
base* foo();
shared_ptr<base> p( foo() );
If base has a public non-virtual destructor and foo returns a type that derives from base, then shared_ptr will fail to call the correct destructor. If the destructor of base is virtual, everything will be fine, if it is protected, the compiler will tell you that there is an error there.
Say we have a base class and a derived. So:
class base {
protected:
~base(){
//...
}
// ...
};
class derived : public base {
// ...
};
And now say that we have this code using the above classes with a smart pointer class:
SmartPointer<base> bptr(new derived());
delete bptr;
I understand that it would prevent slicing of the derived object by calling the destructor of derived, but how does it know to do that? Wouldn't the reference stored in the smart pointer be that of type base*? Does it traverse some kind of hierarchy tree, cast that pointer to derived* and then call delete? Or is there some other thing that I don't know about?
The implementation is supposedly threadsafe, non-intrusive, and reference counting.
YES, the classes that you see are akin to the ones that I'm testing against. There is apparently a way to do this with THESE GIVEN classes. The main idea as to how is mentioned in my question above, but I'm not sure as to how one such an implementation would work.
First thing is that as it stands the code will not work. The destructor of base must be at the very least protected (or derived classes be friends of the base). A private destructor means that the compiler will not allow you to write the destructor for the derived classes. Now, assuming that you have a protected destructor... (Rembember, if you design a class to be extended, provide either a public virtual destructor or a protected non-virtual!)
All depends on the implementation of the SmartPointer, in particular std::shared_ptr (or the boost counterpart boost::shared_ptr) are able to manage that situation cleanly. The solution performs some sort of partial type erasure of the type for destruction purposes. Basically, the smart pointer has a templated constructor that accepts any pointer that can be assigned to a base pointer, but because it is templated it knows the concrete type. At that point it stores a synthetic deleter function that will call the appropriate destructor.
For simplicity, using std::function:
template <typename T>
void delete_deleter( void * p ) {
delete static_cast<T*>(p);
}
template <typename T>
class shared_pointer {
T * ptr;
std::function<void(void*)> deleter;
public:
template <typename U>
shared_pointer( U* p, std::function<void()> d = delete_deleter<U> )
: ptr(p), deleter(d)
{}
~shared_pointer() {
deleter( ptr ); // call the stored destructor
}
};
The code is for exhibition only, it would have to be tweaked for production (where to store the function, reference counting...), but it is enough to give you the idea: in the only function where the exact type of the object is known (when creating the smart pointer), you create a wrapper that will call the exact version of the destructor that you need (providing some short of type erasure), then just leave it around and when you need to delete the object call it instead of the delete operator.
This can also be used to manage other resources that require calling a special method instead of delete:
// exhibition only!
shared_pointer<Foo> p( Factory.create(), &Factory::release );
Again there should be quite a lot of work before making this production ready.
Dependency on std::function which is used to simplify the erasure, can be eliminated from the problem. In the simple case (only memory allocated with new and freed with delete is supported in the smart pointer), then just provide a deleter base class with a single virtual operator()(void*), and then refactor the existing delete_deleter into templated derived classes from deleter that override operator()(void*) with the current implementation. If you need to go for the general case (hold any type of resource) it is not worth the effort, just use std::function or boost::function.
Well first of all, your destructor shouldn't be private or that won't compile at all. Secondly, if you're using a "smart pointer", you probably should not be deleting the pointer by hand at all (I don't know what implementation you're using though, but this strikes as odd to me).
Anyways if you're curious how the derived class' destructor gets called when the object is deleted through a pointer to the base class, the answer is polymorphism. But you're missing virtual declaration from your destructor, right now your code would not call the derived class' destructor.
How most C++ implementations implement this is through a virtual table.
If you using any of boost smart pointers or some other which is not friend of your Base class, then this code wouldn't compile, because destructor of Base class is protected (which is same as private for other independent from Base classes).
Now let's consider that you make SmartPointer<Base> friend of Base. This case the code will work, but it wouldn't call destructor of Derived but destructor of Base, because here your Base class is not polymorphic. You should declare destrucotr of Base as virtual. In last case the correct destructor will be called when your smart pointer is deleted.
this program is invalid.
1) the dtor of base is private
2) the dtor of base is not virtual
to answer your question: you need to correct #1 and #2. then the dtor will be called using dynamic dispatch (which will invoke each dtor in reverse order of construction).
without making those corrections, the only way SmartPointer could know to call derived's dtor in this example, and in a defined manner, is if SmartPointer was overly clever (or tedious to use).
Your base class desctructor needs to be virtual to ensure that destructor of derived class is called when deleting via base pointer.
Wikipedia entry on virtual desctructors