How to show a C++ class doesn't own a pointer member?

Viewed 1076

I have a class with a pointer member, something like:

class MyClass
{
    public:
    void setPointer(Pointer* ptr){_pointer = ptr;}

    private:
    Pointer* _pointer{nullptr};
};

MyClass doesn't own the memory to _pointer. It just has a pointer to invoke methods it requires.

I started to write ~MyClass() and I fortunately I realised it shouldn't delete _pointer because it doesn't own it.

What is the best way to show MyClass doesn't have ownership of that pointer?

EDIT:

Should I use a unique_ptr in the owner class and a shared_ptr in MyClass, or should they both use shared_ptr?

4 Answers

Future

At the moment, std::observer_ptr is in discussion to express exactly those semantics: non-owning, passive pointers. See also this thread for its purpose.


Present

In idiomatic C++ (latest since 2011), it's quite uncommon to still have raw pointers that own the object they're pointing to. The reason is that new/delete come with a lot of pitfalls, while there are better alternatives such as std::unique_ptr and std::shared_ptr. Those do not only bring the semantics needed, but also express the intent in code (unique or shared ownership, respectively).

Eventually it depends on code style of your environment, but I would say until a standard best-practice is established, T* is a good convention for non-owning, passive pointers.

The Coreguidelines come with a support library that was a owner to mark raw pointers as owning. In that context they write:

The "raw-pointer" notation (e.g. int*) is assumed to have its most common meaning; that is, a pointer points to an object, but does not own it. Owners should be converted to resource handles (e.g., unique_ptr or vector) or marked owner.

owner<T*> // a T* that owns the object pointed/referred to; may be nullptr.

owner is used to mark owning pointers in code that cannot be upgraded to use proper resource handles.

I read that as: A raw pointer does not need to be marked as not-owning, because nowadays raw owning pointers should be the exception. Hence, it is owning raw pointers that need to be highlighted.

Of course this only applies when you consistently avoid owning raw pointers in your code.

With a comment.

// A class wrapping (but not owning) a pointer.
class MyClass
{
public:
    void setPointer(Pointer* ptr){_pointer = ptr;}

private:

    // Not owned by the class
    Pointer* _pointer{nullptr};
};

Seriously, don't be afraid to document your code with comments. That's what they're there for.

What is the best way to show MyClass doesn't have ownership of that pointer?

The lack of ownership of a resource (such as pointer to dynamic object) which you store is shown by not deallocating (deleting) the resource. Conventionally, you should never store bare identifiers (such as bare pointers) to owned resources except in a RAII wrapper (such as a smart pointer) whose sole purpose is to own and deallocate the resource.

Not taking ownership of a pointer passed to you is shown by accepting a bare resource identifier - as opposed to a RAII wrapper. This convention cannot be followed when providing a C compatible interface nor in the constructor / assignment of such RAII wrapper, which does take ownership of a bare pointer. In such non-conventional cases, the exception must be documented carefully.

The client is shown that ownership is not transferred to them by returning a bare resource identifier. This has similar exceptions with C interface as well as in the interface of the RAII wrapper, if it supports releasing ownership.

All of these can be clarified with extra documentation.


Should I use a unique_ptr in the owner class and a shared_ptr in MyClass,

No. Unique pointer means that it is the only owner of the resource. The ownership cannot be shared with other pointers. If you use a unique pointer, then you can use a bare non-owning pointer in MyClass.

or should they both use shared_ptr?

That is an option. It safer than the unique + bare pointer, because there is no need to guarantee that the lifetime of the unique pointer is longer than the lifetime of the bare pointer. The downside is some runtime overhead.

Related