13

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).

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • 1
    possible duplicate of [shared_ptr magic :)](http://stackoverflow.com/questions/3899790/shared-ptr-magic) – Armen Tsirunyan Jul 09 '11 at 12:36
  • @Armen: This is not a duplicate, he is not asking how `shared_ptr` does it, but whether you should use a virtual destructor *knowing* that `shared_ptr` does that magic. – David Rodríguez - dribeas Jul 09 '11 at 12:39
  • 1
    @David: No, he doesn't. He says he will use a virtual destructor anyway. He's asking whether it's OK not to have one. So it is a duplicate – Armen Tsirunyan Jul 09 '11 at 12:41
  • 2
    Yes it's true. However I personally would worry about doing it. One day I'm going to decided "oh this doesnt need a shared_ptr, I'll just use a pointer to the base class", and everything breaks subtly. I'd regard it as fragile code that external code making reasonable assumptions about how classes are implemented could easily break, and not do it unless I could prove there was a requirement that could only be met by doing so. – jcoder Jul 09 '11 at 12:42
  • Well... if you have `shared_ptr` instances that could point to a derived type, then you're almost certainly using `virtual` member functions anyway, right? Otherwise, what's the point? So in that case, making the destructor virtual doesn't cost anything... except maybe a little extra typing :) – Karl Knechtel Jul 09 '11 at 13:57
  • Does your example actually make non-trivial use of the shared_ptr's type erasure? The deleter in all three cases is just `delete(void*)`. I don't see the connection between a custom deleter and deletion-through-base-pointer. – Kerrek SB Jul 09 '11 at 14:59
  • 1
    @Kerrek: No, the deleter is different for all three cases. They all maybe take a `void*`, but cast it to the right type, `int`, `float` and `char` respectively. – Xeo Jul 09 '11 at 15:02

1 Answers1

14

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.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • "I would declare it virtual anyways, [...]". :) Good point about not being able to control all instantiation points. Though, you can always make-do with a named constructor, but that probably doesn't look so nice. – Xeo Jul 09 '11 at 12:42
  • 1
    Warning: Protected destructors do not currently answer true for `is_nothrow_destructible::value` even if they don't throw an exception. For that I reason I would favor the public option. – Howard Hinnant Jul 09 '11 at 13:07
  • @Howard: thanks for info about `is_nothrow_destructible`. It seems to do the right thing. Why would you let the fact that it currently correctly reports "not destructible" for a non-destructible thingy, make you change the thingy to destructible? – Cheers and hth. - Alf Jul 09 '11 at 14:24
  • 1
    @Xeo: Can you really control instantiation? If your class is meant to be a base, then it means that I can write my own extension, and you cannot control how I allow users to instantiate my objects. – David Rodríguez - dribeas Jul 09 '11 at 14:43
  • @DavidRodríguez-dribeas In the end, the other programmers can always do as they wish. The best you can really do is a documented policy and roadblocks for attempts to circumvent. – Deduplicator Aug 11 '20 at 16:28