0

Imagine having a vector of shared pointers

typedef vector< shared_ptr< classA > > PointerVector;

And a Class B which has as a member a vector of shared pointers as well, and a method that pushes back in this vector an already dereferenced shared Pointer.

ClassB
{
public:
    void add(classA &ref);
private:
    PointerVector vector;
}

Assume that main feeds the add function with a dereferenced shared_ptr< classA > instance:

in main:

shared_ptr< classA > sharedptr ( new classA );
ClassA &ref = *sharedptr;
ClassB B;
B.add(ref);

Is there a way to implement the add function to accept a dereferenced shared pointer and convert it to the initial shared pointer? and then push it back in the matrix?

This is what i am trying to do:

void ClassB::add(classA &ref) {
    shared_ptr< classA > ptr = &ref;
    vector.push_back( ptr );
}

NOTE1: When the classA is added to the vector, i want the shared pointer count increased by one. I dont need to create another shared pointer, i want to "find" the shared pointer the object previously had.

NOTE2: i cannot use make_shared because i am currently in tr1

2 Answers2

4

Generally you cannot just "find" pointers to object, as there is nothing keeping track if particular object is pointed to or not. You should either devise such mechanism yourself or use one provided by standard library: enable_shared_from_this.

Step 1: derive your class from std::enable_shared_from_this and provide a member function to get pointer:

#include <memory>

struct ClassA: std::enable_shared_from_this<ClassA>
{
    std::shared_ptr<ClassA> get_pointer() 
    {
        return shared_from_this();
    }
    //...
};

Step 2: Make sure all objects you want to get pointers for are managed by shared_ptr:

std::shared_ptr<ClassA> original_ptr(new ClassA);

Step 3: call function defined in step 1 whenever you want to get a shared pointer to object:

void ClassB::add(classA& ref) {
    shared_ptr<classA> ptr = ref.get_pointer();
    vector.push_back( ptr );
}
Revolver_Ocelot
  • 8,609
  • 3
  • 30
  • 48
  • 1
    In addition I would make the constructor of `ClassA` private and add a static factory method that returns `shared_ptr` to prevent the undefined behaviour if `shared_from_this()` is called for an object not managed by a `shared_ptr`. – zett42 Sep 23 '17 at 18:48
2

There is a way to do this, but needing this might mean there is a design flaw.

Why it doesn't work

A shared_ptr needs a reference counter to work. This counter is kept in a control block that is created when the first shared_ptr is created. When you copy a shared_ptr, the copy doesn't just copy a pointer to the managed object, it also copies a pointer to the control block. This allows it to access the reference counter and to destroy the block when it destroys the object.

A function that takes ClassA & does not have access to the control block. Attempting to create a new shared_ptr will create a new control block with a reference count of 1. When either reference counter reaches 0, the object will be destroyed. Therefore, it will be destroyed twice, which is undefined behavior.

How it can work

As mentioned in the comments already, std::enable_shared_from_this solves your issue. You have to make your class derive from it.

ClassA : std::enable_shared_from_this<ClassA> {...}

Then you can call ClassA.shared_from_this() to get a shared_ptr that is a copy of existing shared_ptrs of the same object.

Why I don't recommend using it

You have to be very careful with enable_shared_from_this. cppreference.com states

It is permitted to call shared_from_this only on a previously shared object, i.e. on an object managed by std::shared_ptr. Otherwise the behavior is undefined (until C++17)std::bad_weak_ptr is thrown (by the shared_ptr constructor from a default-constructed weak_this) (since C++17).

So you have to make sure the object is already shared before trying to create a new shared_ptr from it with the shared_from_this method.

Furthermore, your function signature accepts ClassA&. It can be called with arguments on the stack; creating a shared_ptr from stack objects is dangerous. If your function needs to share ownership, it should only accept a shared_ptr<ClassA> so as to communicate what its intention is. This makes it self-documenting and harder to mishandle while sacrificing nothing.

patatahooligan
  • 3,111
  • 1
  • 18
  • 27
  • _"So you have to make sure the object is already shared"_ and _"It can be called with arguments on the stack"_ - This can easily be fixed by making the constructor(s) private and adding a static factory method to return `shared_ptr`. – zett42 Sep 23 '17 at 18:54
  • This forbids creating `ClassA` objects anywhere on the stack, even in parts of the program that don't needs `shared_ptr`s. It also doesn't address the fact that `ClassB::add`s signature is confusing for the caller. I just don't see any reason for `add` not to change. – patatahooligan Sep 23 '17 at 18:58