2

I have a class, DevicePointer, that encapsulates a std::shared_ptr<Device>. Various classes that need to hold a pointer to a Device derive from DevicePointer. Before I started using shared_ptr, DevicePointer had an ::Expose() function that would return a raw pointer to a Device. Now I'm using shared_ptr to hold the Device pointer, I'm not sure how to return it. Note that the only reason ::Expose should be called is to dereference the pointer.

This is what the original Expose looked like:

Device * Expose() const  { return MyDevice; }

and would be used like this:

Device::Expose()->ExecuteFunction(a, b, c);

Now MyDevice is a std::shared_ptr<Device>, I'm not sure how to return it for dereferencing. The obvious choice is:

std::shared_ptr<Device> Expose() {
    return MyDevice;
}

but I worry about performance, particularly the creation of a new temporary std::shared_ptr. So I need some way of saying "you can dereference this pointer but you can't copy it". The original still needs to be shared because many objects will hold a reference to it.

I hope I've articulated my question adequately. Thanks.

Robinson
  • 9,666
  • 16
  • 71
  • 115
  • Please clarify your question. Your function `Device & Expose()` is not returning a raw pointer, it is returning a reference. If you want access to the underlying pointer from a shared pointer you can use the `get` method (cf. http://en.cppreference.com/w/cpp/memory/shared_ptr). – Escualo Nov 05 '13 at 17:18
  • OK, edited to be less ambiguous. It's now about returning a pointer only. – Robinson Nov 05 '13 at 17:23
  • I updated my answer to explain how to prevent no copy for MyDevice. – Raxvan Nov 05 '13 at 17:41
  • May I ask why do you need to expose the underlying pointer? That is, why do you need to do this: `Device::Expose()->ExecuteFunction(a, b, c)`, instead of simply doing this as follows: `Device->ExecuteFunction(a, b, c)`? Notice that `std::shared_ptr` will correctly handle the `->` operator. – Escualo Nov 05 '13 at 20:12

3 Answers3

2

This will not hurt the performance

Device & Expose() {
    return *MyDevice.get();
}

Edit:

To make the object not copyable:

class Device
{

private:
    //compiler will throw errors when copy constructor or = is called in the code
    Device(const Device &)
    {}
    void operator = (const Device &)
    {
    }
};

Edit (2018-09-05):

As of c++11 you can explicitly delete copy constructor or assignment operator:

class Device
{
public:
    Device(const Device &) = delete;
    Device& operator = (const Device &) = delete;
};
Raxvan
  • 6,257
  • 2
  • 25
  • 46
  • 1
    There is no need to call `.get()` here. –  Nov 05 '13 at 17:26
  • I think that's it (minus the .get()). Except what Peter said about not being able to prevent callers from taking a copy. Hmmmm. – Robinson Nov 05 '13 at 17:35
  • Yes you are right , no need to call the `get` , i usually avoid not calling it so that other programmers know that `MyDevice` is a share/intrusive ptr. – Raxvan Nov 05 '13 at 17:36
1

(EDIT: The first paragraph here refers to your original question, where you were dereferencing the pointer in the function, and returning a reference.)

Dereferencing the pointer like that doesn't seem very safe, because it puts the onus on the caller to determine the safety of the operation without being able to see the pointer. If the pointer's null, it's going to go very wrong.

As such, I'd recommend just returning a copy of the shared_ptr. The performance impact shouldn't be significant unless you're calling it hundreds of times per second.

Unfortunately there's no way to prevent copying. Whether you return a pointer or a reference, the caller can easily take a copy.

Peter Bloomfield
  • 5,578
  • 26
  • 37
  • Yes, sorry about that. I edited it because I was confusing the issue by talking about pointers but returning it as a reference. So I changed it to just be about a pointer to an object. – Robinson Nov 05 '13 at 17:26
1

If your application is multithreaded and your objects can be 'destroyed' at any time in the async manner then there is no other choice than to return shared_ptr copy. If you are sure about lifetime of your objects then you can return reference to your shared_ptr. But this can become the pain in the ass after your code evloves. The same is with dereferenced pointer (shared_ptr::get) because it would be very hard in future to support that code when it become more complicated and objects' lifetimes become hard to track.

Alexey Zhivotov
  • 101
  • 1
  • 5