0

I have a couple of questions for smart pointers that earlier I didn't give them any credit.

  1. What does mean to own an object , to point to a object and to manage a object in the world of smart pointers? Earlier I thought that the one who owns the object, also points to it and manages the object. Now, I know that a smart pointer can own an object, but point to another object (alias constructors). Here I found a really good explanation for what owning an object mean -> http://www.umich.edu/~eecs381/handouts/C++11_smart_ptrs.pdf , but still I can't make difference between this 3 terms.
  2. If the pointer owns an object, but point to another object, which object does he manage? The one he owns it, or the one he points to, or both? What's the point of owning an object, but not pointing to it?
  3. When are two smart pointers equal? Can two pointers own a same object and be different in a same time? I'm not interested in their value equality, but regarding the ownership.
  4. Why is ownership order important (beside for using the pointers as keys in containers)? I guess this is relevant only for shared_ptr.

Everything began with trying to understand owner_before, now I'm more confused than before I began exploring this topic.. :(

Alek
  • 91
  • 8

3 Answers3

1

I think all of your confusion comes from the "aliasing constructor":

template <typename U>
shared_ptr(const shared_ptr<U>& x, element_type* p)

What's the use of this thing? Well, it's rarely used, but what it does is to "share" ownership of some object x but when you dereference it you will get p instead. That's all. It will never delete p or do anything else to it.

It might be useful if you have something like this:

struct Foo {
    Bar bar;
};

struct Baz {
    Baz(shared_ptr<Bar> bar) : m_bar(bar) {}
    shared_ptr<Bar> m_bar;
};

int main()
{
    auto foo = make_shared<Foo>();
    Baz baz(shared_ptr<Bar>(foo, &foo.bar));
}

Now baz gets to manage the lifetime of foo without knowing that's what it's doing--it only cares that it manages the lifetime of a Bar, but since our bar is part of foo, we can't destroy foo without destroying bar, so we use the aliasing constructor.

But actually, we don't do that, because this use case is very rare.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Hmm, and who would take care of p, if p is not member of x? I know is a stupid thing, but I'm looking just the worst case scenario. See the next example, there would be a memory leak I guess.. std::shared_ptr foo = std::make_shared(); std::string *str = new std::string("example"); std::shared_ptr p2(foo, str); It's compiled :) – Alek Dec 14 '16 at 13:46
  • @Alek: `p` being a member or otherwise managed by `x` is the entire use case. There is no "worst case scenario." You should never use `new` directly in modern C++ code unless you "know what you're doing." – John Zwinck Dec 14 '16 at 13:50
0

Old "raw" pointer:

someType* pR = new someType(param1, param2); //pR is a pointer
MyOwner.TakeOwnershipOf(pR); // Now MyOwner is the owner, **the one who ONLY should call 'delete pR;'**

Smart pointer:

std::shared_ptr<someType> sp = std::make_shared<someType>(param1, param2);

"sp" is now the owner (there's no "pR" in code, but internally it is). And you don't need to call "delete pR". It's an object that internally stores a pointer to pR and delete it when becomes no more needed.

sp is an object. "sp->any" is exactly as "pR->any". This can confusse you about sp being also a pointer. No, it happens that "->" is overloaded.

More overloaded:

sp.get() is the same as pR. *sp is the same as *pR

sp1 == sp2 is the same as pR1 == pR2 and the same as sp1.get()==sp2.get()

The good thing about shared_ptr is, for example, when you need to store the same pointer in several containers. If some container is deleted, the stored pointer (sp, who owns pR) should also be deleted. This will invalidate the pointer stored in other container. Instead of inspecting other containers about this pointer existence, shared_ptr takes in charge of it. This is, you can safely delete sp because pR will be only deleted by the last container that stores sp.

Ripi2
  • 7,031
  • 1
  • 17
  • 33
0

Ownership

Your confusion is because ownership is not something which the compiler can verify; you, as a programmer, need to deduce it.

We can say any object p owns q if the existence of p guarantees the existence of q (preferably without memory leaks).
The simple case is direct ownership, where deallocating p would also deallocate q, e.g. if q is a member of p, or q is explicitly deallocated with delete in p's destructor.
Smart pointers make this obvious to people. If q is stored in a std::unique_ptr member of p, we know that p owns q. You don't need to search around for the (possibly missing or duplicated) delete statement.

Ownership is also transitive, if p owns q and q owns r, then p must own r.

Aliasing

If p directly owns q, and we want to create a shared_ptr that owns q, then it must also own p. Otherwise, if p is destroyed, then q will be too, despite the existence of our shared pointer.

This is what the aliasing constructor for std::shared_ptr does (deomonstrated in John's answer).

It extends q's lifetime by extending p's lifetime, so we have a pointer to q which actually owns p. We are asserting to the compiler that p in fact owns q, so the shared ptr transitively owns q.

If p doesn't own q, then your program will compile, but it is broken, just like if you manually call delete twice.

Comparisons

For stl smart pointers, comparisons are passed to the raw pointer. So smart pointers are equal if they dereference to the same object, and comparisons are about memory location. There aren't any defined behaviors specifying memory locations, so there isn't much you can use it for other than storing in a map or set.

Community
  • 1
  • 1
Joseph Ireland
  • 2,465
  • 13
  • 21