Search code examples
c++c++11shared-ptr

What is difference between "owned pointer" and the "stored pointer" for std::shared_ptr?


As per this documentation, which says (emphasis mine):

http://www.cplusplus.com/reference/memory/shared_ptr/owner_before/

Unlike the operator< overload, this ordering takes into consideration the shared_ptr's owned pointer, and not the stored pointer in such a way that two of these objects are considered equivalent (i.e., this function returns false no matter the order of the operands) if they both share ownership, or they are both empty, even if their stored pointer value are different.

The stored pointer (i.e., the pointer the shared_ptr object dereferences to) may not be the owned pointer (i.e., the pointer deleted on object destruction) if the shared_ptr object is an alias (alias-constructed objects and their copies).

What is the difference between "owned pointer" and the "stored pointer" of std::shared_ptr?

I would be grateful to have some help with this question.

Here is some related code (check http://cpp.sh/27auqq):

// enable_shared_from_this example
#include <iostream>
#include <memory>

struct C : std::enable_shared_from_this<C> {int a; int b; };

int main () {
  std::shared_ptr<C> foo, bar;

  foo = std::make_shared<C>();

  bar = foo->shared_from_this();
  
  std::shared_ptr<int> p1(foo, &foo->a);
  std::shared_ptr<int> p2(foo, &foo->b);
  
  *p1=5;
  *p2=9;
  
  std::cout << p1.use_count() << std::endl;
  std::cout << foo->a << std::endl;
  std::cout << foo->b << std::endl;

  if (!foo.owner_before(bar) && !bar.owner_before(foo))
    std::cout << "foo and bar share ownership" << std::endl;
    
  if(!p1.owner_before(p2) && !p2.owner_before(p1))
    std::cout << "p1 and p2 share ownership" << std::endl;
    
    if(!p1.owner_before(foo) && !foo.owner_before(p1))
    std::cout << "p1 and foo share ownership" << std::endl;
      

  return 0;
}

Here is the output:

4
5
9
foo and bar share ownership
p1 and p2 share ownership
p1 and foo share ownership

Solution

  • In order to support things like converting a shared_ptr<Derived> to shared_ptr<Base> even when no virtual destructor exists (which is allowed in that case even if it isn't a great idea in general for dynamically allocated classes), and for a few other features, shared_ptr needs to have one pointer to the object designated by each specific instance in that instance, and another one in the control block. The control block is used:

    • for counting "strong references" (owners of the dynamically allocated used ressource managed by the shared_ptr) and "weak references" (owners of the control block but not of the managed used resource);
    • to remember what to delete (pointer to the owned object) and how to delete it. The control block holds the exact pointer value that was passed to the original shared_ptr constructor.

    Usually that value is used only for the purpose of deletion. Its value is examined (compared) only when you use owner_before. All other functions examine the pointer in each shared_ptr instance, not the pointer in the control block.