6

Looks like shared_ptr hides the lack of virtual destructor in base class.

For this code:

class IInt {
public:
    /*virtual*/ ~IInt() {} // Lack of virtual destructor!
    virtual int get() const = 0;
};

class Int : public IInt {
public:
    Int(int a) : a(a) {}
    ~Int() { cout << "~Int:" << a << endl; }
    int get() const override { return a; }
private:    
    int a;
};

shared_ptr - virtual destructor not needed

At the end of this block - shared_ptr<IInt> properly destructs the c101 - quite surprisingly the lack of virtual destructor in IInt is not a problem here:

{
    std::shared_ptr<IInt> c101 = std::make_shared<Int>(101);
    std::shared_ptr<IInt> c102(new Int(102));
    cout << c101->get() << endl;
    cout << c102->get() << endl;
}

Output:

101
102
~Int:102
~Int:101

unique_ptr - virtual destructor needed

{
    std::unique_ptr<IInt> c103 = std::make_unique<Int>(103);
    std::unique_ptr<IInt> c104(new Int(104));
    cout << c103->get() << endl;
    cout << c104->get() << endl;
}

Output - lack of proper destruction:

103
104

Question:

I am not asking for explanation how it happens it works that way. I hope I properly understand that all is the matter of destruction differences in shared_ptr and unique_ptr.

  1. shared_ptr has destroying object attached to its control block - and this object once created is still there.
  2. unique_ptr - destroying object is attached to every single unique pointer and its statically linked - and not change - cannot be taken from other pointer.

I am rather asking:

  1. Is that somewhere documented? This difference in behavior? Any note in C++ standard saying that this can be confusing.
  2. Are there any plans to change it?

Of course please correct and/or complete my short explanation if that is needed.

PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • 7
    1, Yes, in the standard. 2. No. This is "working as intended". And this isn't specific to `make_shared`. The following works, too: `shared_ptr p = shared_ptr(new Der);`. The key is to get the correct deleter; once you have it, it stays. – Kerrek SB Mar 24 '15 at 10:10
  • 5
    As for the rationale, think of it this way: you are already paying the cost of type erasure in `shared_ptr`, which requires a dynamic dispatch. So instead of requiring *another* dynamic dispatch for the destructor, the two dispatches are "folded into one", if you will. – Kerrek SB Mar 24 '15 at 10:12
  • 1
    Y u no answer @KerrekSB ? =) – luk32 Mar 24 '15 at 10:22
  • 2
    @PiotrNycz I don't think it's there explicitly. It's more of a consequence (albeit intended!) of the rules for deleters. – Angew is no longer proud of SO Mar 24 '15 at 10:32
  • 1
    @PiotrNycz look at the copy ctor and `operator=`. Both of them copy the source deletor. Now look at how the deletor is defined during from-ptr and `make_shared` -- it is defined in terms of the object a shared pointer is referring to. The result is that a shared pointer to base can use the deletor stored when the shared pointer to base was created. Heck, you could use the "god mode" `shared_ptr( shared_ptr, T* )` ctor and create a view of a member of a class, and the dtor for the enclosing `U` will naturally be called. – Yakk - Adam Nevraumont Mar 24 '15 at 21:01

0 Answers0