Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this a valid use of intrusive_ptr?

Within my code I follow two rules when it comes to intrusive_ptrs:

  • Passing a raw pointer by value implies that the raw pointer is guaranteed to be valid during the lifetime of that function.
  • If the raw pointer is to be stored and used beyond the lifetime of the function, it should be stored in an intrusive_ptr.

Many Internet commenters have written that shared_ptr should be preferred over intrusive_ptr except when working with third party code. However, intrusive_ptr obviates passing around smart pointers since you can create an intrusive_ptr from a raw pointer, as when the object is needed beyond the lifetime of the function.

I'm just concerned that I'm missing something because nothing that I've read has made this point about intrusive_ptrs, and that most people seem to prefer shared_ptrs even though they introduce memory overhead and also problems when using enable_shared_from_this and inheritance.

like image 345
May Oakes Avatar asked Apr 18 '11 23:04

May Oakes


2 Answers

Passing a raw pointer around in a public API that has ownership semantics should be done only rarely, and only when absolutely necessary. E.g. interfacing with code whose interface you can not change.

Passing around a raw pointer in a private API, e.g. within the members of a single class is no problem.

Consider these three functions:

void f(A* a);
void g(std::unique_ptr<A> a);
void h(std::shared_ptr<A> a);

The ownership semantics of f is not clear. If you are the client of f you need to read the documentation to find out if f is going to deallocate a, or ignore ownership issues for a.

The ownership semantics of g is clear. When you call g you pass the ownership of a to g and you are no longer responsible for it. g will either deallocate a or transfer ownership of that resource somewhere else.

The ownership semantics of h is clear. When you call h, you and h become co-owners of a. Last one out turns off the lights.

void q(boost::intrusive_ptr<A> a);

q has the same ownership semantics as h. The main difference is that the following free functions must exist:

intrusive_ptr_add_ref(A*);
intrusive_ptr_release(A*);

If you are the author of f and you call these functions on a, you should document that you do so. Your clients won't necessarily know that you are. And if you are a client of f, you have no way of knowing if f will call these functions unless you read its documentation.

If you are the author of f and you intend on calling the intrusive_ptr_* functions, you can make that explicit in your interface by coding q instead.

But usually there is not a compelling reason to impose on the author of A to write the intrusive_ptr_* functions. And you can get the same ownership semantics as q by writing h instead, without imposing any further requirements on A.

On Memory Overhead

If you create your shared_ptr with:

 shared_ptr<A> p = make_shared(arguments-to-construct-an-A);

then your shared_ptr will have the exact same memory overhead as an intrusive_ptr. The implementation will allocate the A and the refcount in the same memory allocation. Your clients need not know or care that your shared_ptr was constructed so efficiently.

like image 97
Howard Hinnant Avatar answered Oct 25 '22 22:10

Howard Hinnant


intrusive_ptr does come with other prices: you have to clutter your actual design with memory management information, you are making things more complicated than required, contributors to your project wont be familiar with your reasoning (and even intrusive_ptr) and you need more documentation.

Also: have you ever benchmarked that the overhead of copying smart pointers is actually affecting your application? I'd rather go with the simple version first and would introduce intrusive_ptr only when really necessary.

like image 38
pmr Avatar answered Oct 25 '22 21:10

pmr