2

I'm running this code in MS Visual Studio 10,

#include <iostream>
#include <memory>
using namespace std;

class A
{
    int i;
public:
    A(int j) : i(j) {}
    ~A() {}
    void fun()
    {
        cout << "A::i = " << i << endl;
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
    A aObj(12);
    std::shared_ptr<A> const pObj (&aObj,
                [] (A* pA) {
                    cout << "lambda deleter" << endl;
                });
    aObj.~A();
    pObj->fun();
    return 0;
}

This prints / holds data members for an object, that has already been deleted, without reporting any type of error.

Please write on:

  1. Why shared_ptr pObj doesn't report (at run-time) that underlying object has already been deleted?
  2. Since I'm creating a const shared_ptr, means can't use it to refer any other object, why lambda is not invoked at object deletion.
  3. Can weak_ptr be helpful in similar cases. weak_ptr is used with semantics that the lifetime of a reference to an object outlives the object it refers to.
masT
  • 804
  • 4
  • 14
  • 29

1 Answers1

20

Why shared_ptr pObj doesn't report (at run-time) that underlying object has already been deleted?

Because shared_ptr is not magic1. It knows when the contained object has been deleted only when it deletes that object. When you use a shared_ptr, you have entered into a contract with shared_ptr. One of the tenants of that contract (indeed, of any contract you enter into with a smart pointer of any kind) is that you don't get to delete the pointer. The shared_ptr instance owns the pointer, and it will delete it, not you.

Violating that contract leads to undefined behavior.

Since I'm creating a const shared_ptr, means can't use it to refer any other object, why lambda is not invoked at object deletion.

Again, the shared_ptr can only know that the contained object is deleted when it deletes it. It knows nothing about the state of the object if you break the contract.

Can weak_ptr be helpful in similar cases. weak_ptr is used with semantics that the lifetime of a reference to an object outlives the object it refers to.

weak_ptr is no more magically endowed than shared_ptr. weak_ptr only knows what the shared_ptr set it was created with knows about. If the shared_ptr doesn't know that the object's been deleted, the weak_ptr won't either.


1 By "magic", I mean doing something that's just not possible in C++. If you want to know that a function has been called (and a destructor is a function call), there are only two ways to do that. Either that function tells you it's been called (by setting some value you can see), or you set up a system whereby people call your function which then calls the other function.

The first system requires a function that is written explicitly to let people know that it's been called. You can't do that with any old function; it has to be designed for that. The second system requires that everyone use your new function and nobody uses the old one. If someone uses the old one directly, your code won't know about it.

The first method is called "intrusive" (because it requires that you write your object in a special way), and smart pointers that use it are called "intrusive smart pointers". The second method is non-intrusive (doesn't require special code for the object). shared_ptr, and all currently standard smart pointers, are non-intrusive. This means you can use them with any object, but you can only use them if you abide by the contract.

C++ does not offer a third way. Therefore, a class that could somehow intrude on a destructor call, a class that could know it has been called without that destructor explicitly telling it that it has been, is not possible. And therefore would be magic.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    I believe it is technically possible to write a custom MM which would track deletion of objects owned by smart pointers, and it is also possible to extend smart pointers with `pointed_object_was_destroyed()` method for callback, but... meh. – Joker_vD Apr 05 '13 at 10:22
  • 1
    @Joker_vD: And that would be *intrusive*; you couldn't do it with any pointer you get with regular old `new`. – Nicol Bolas Apr 05 '13 at 10:26
  • *One of the tenants of that contract (indeed, of any contract you enter into with a smart pointer of any kind) is that you don't get to delete the pointer.* I disagree, although it's a matter of definition of "smart pointer": Smart pointers in general are classes that provide pointer operators and behaviour that is "smarter" than plain C pointers. The best known smart pointers are the ones taking care of memory (de-)allocation, take ownership of objects and are subjects to the mentioned contracts. But there are others, e.g. iterators that have smart increment. – Arne Mertz Apr 05 '13 at 12:10
  • 1
    @ArneMertz: Yes, and are you allowed to delete the object associated with such a smart incrementing pointer without notifying the smart pointer? Or would that yield undefined behavior, since the smart pointer thinks it's still alive? Yes, there are smart pointers that don't delete the object for you. But I don't know of a smart pointer that lets you delete an object without telling the pointer about it. – Nicol Bolas Apr 05 '13 at 12:12
  • I was not referring to "tell or don't tell". I was referring to *you don't get to delete the pointer*, which is wrong. In the case of iterators it's not the iterator but the container that deletes the object. With iterators the contract does *not* say "you don't delete the pointer", although it often says "if you delete it, you invalidate the iterator", meaning "you shall not use it any more". But I still don't have to explicitly tell the iterator that I deleted the object. – Arne Mertz Apr 05 '13 at 12:44