Can deriving a class from 'enable_shared_from_this' increase performance? - c++

make_shared is more performant than separately calling new and creating a shared_ptr because make_shared allocates space for the reference count and weak count in the same memory block as the client object instance (effectively giving the shared_ptr most of the performance benefits of an intrusive_ptr).
enable_shared_from_this gives a shared pointer without having a reference to any shared pointer. Therefore things like the reference and weak count have to be somehow accessible from inside the client object. Therefore, it would be sensible for enable_shared_from_this to cause an intrusive count similar to make_shared.
However, I have no idea how something like that might be implemented (and I'm not sure I'd follow what was going on in there even if I look at the actual source).
Would it make sense then (for performance reasons) to tag my class with enable_shared_from_this if I know it's only ever going to be used as a shared_ptr and never as a raw object?

I have never dug into the details of implementation, but for shared_from_this to work, the object must already be managed by an external shared_ptr, so it is to some extent unrelated. I.e. the first shared_ptr might have been created with make_shared in which case the count and object are together (as you say intrusive pointer like), but that does not need to be the case.
My first guess is that enable_shared_from_this adds the equivalent of a weak_ptr, rather than a shared_ptr. EDIT: I have just verified the implementation in gcc4.6:
template <typename _Tp>
class enable_shared_from_this {
...
mutable weak_ptr<_Tp> _M_weak_this;
};

I don't believe so. How enable_shared_from_this is implemented isn't strictly defined, but an example implementation is present in the standard that corresponds to how boost does it. Basicaly, there is a hidden weak_ptr that shared_ptr and friends has access to... anytime a shared_ptr is given ownership of an object derived from enable_shared_from_this, it updates that internal pointer. Then shared_from_this() simply returns a strong version of that weak pointer.
In the general case, the implementation can't really assume that nobody will ever go shared_ptr(new T) instead of using make_shared, so an intrusive reference count would be risky. You can instead make that guarentee yourself, by whatever means you use to construct the objects in the first place.

Boost's enable_shared_from_this does not change the implementation of shared_ptr itself. Remember that shared_ptr is paired with weak_ptr - this means that, even after the object is deleted, the tracking data may need to remain around to tell the weak_ptr that the object is dead. As such, it can't be embedded into the object, even with enable_shared_from_this. All enable_shared_from_this does is embed a pointer to said tracking data into the object, so a shared_ptr can be constructed with just a pointer to the object.
This is also why intrusive_ptr cannot have a weak_intrusive_ptr variant.

Related

Why calling shared_from_this calls std::terminate

Consider this code:
class A : public std::enable_shared_from_this<A>
{
public:
std::shared_ptr<A> f()
{
return shared_from_this();
}
};
int main()
{
A a;
std::shared_ptr<A> ptr = a.f();
}
This code terminated in Visual Studio 2017. I guess I am doing something wrong here. Can anyone help me with this? I want a shared_ptr on a created by shared_from_this().
Because a is not a owned by a shared pointer. From cppreference:
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).
You cannot use shared_from_this to generate a new shared pointer. You already have to have an existing shared pointer to get a new one:
std::shared_ptr a(new A());
auto ptr = a->f(); // ok, 'ptr' shares ownership of newed object with 'a'
The issue is fundamental in the design (not technical details)
Whatever the exact specification of that C++ standard version you are using, what you are trying to do is impossible. Knowing the fine details of the specification of shared_from_this isn't needed to conclude that the code contains a design contradiction: just understanding the intent, which is to obtain a shared_ptr<A> to this, inside a member function called on a, an automatic object, is enough to determine that the design is in error.
In fact, any attempt at making an owning smart pointer (incl. but not limited to unique_ptr, shared_ptr) that points to (that owns) an object with "scoped" lifetime, that is an object whose lifetime is defined by the scope of the declaration of the object and ended by exiting something:
automatic objects (lifetime ends when exiting the scope),
namespace scope objects, static object members of classes (lifetime ends at program exit),
non static members of classes (lifetime ends when the body of the destructor of the containing class object exits),
is a design error, because the:
these objects were not created with any variant new (plain operator new or nothrow variant) that permits the call of delete on the result);
the only case where C++ allows you to destroy such object is by destruction followed (preferably immediately) by reconstruction with placement new of an object with the same complete type, which is obviously not the job of an owning smart pointer;
the compiler will destroy that object when program execution reach the exit point (exits scope, exits destructor, or std::exit, or return from main), no matter what (even if your owning smart pointer already took care of it); trying to destroy an already destroy object is not OK.
This includes constructing a smart pointer that owns (i.e. that promises to delete) a member of a class instance that was dynamically allocated:
struct A {
int m;
};
#define OK 1
void f() {
A *p = new A;
#if OK
std::shared_ptr<A> own (p); // fine
#else
std::shared_ptr<int> own (&p->m); // bad
#endif
}
Here the lifetime of the object pointed to by p is managed dynamically; the timing of the destruction of determined explicitly, by program code, and the lifetime of the unique member m is intrinsically linked to the lifetime of the A object; but the member itself need not be destructed explicitly and shall not be deleted. If the OK preprocessor constant is 1, all is well; if it is 0, you are trying to manage explicitly the lifetime of a member, which is unsound.
About the term "explicit" call to delete: although th delete operator never appears in the code, its call is implicit on the use of std::shared_ptr; in other words, std::shared_ptr explicitly uses delete, so use of std::shared_ptr (or other similar owning smart pointers) are indirect use of delete.
Safely sharing ownership with a smart pointer
The only safe way to share ownership of a shared_ptr is to make one from another shared_ptr, directly, or indirectly. This is the fundamental property of shared_ptr: all instances pointing to the one object must be traceable back to the one instance that one constructed with a raw pointer (or alternatively with make_shared).
This is a direct consequence of the fact that the ownership information (usually a reference count, but could be a linked list if you love inefficient implementations) is not inside the managed object, but inside the information block created by shared_ptr. This is not a property of just std::shared_ptr, it's a fact of life of all these externally managed objects, without a global registry, it's impossible to find the manager.
The basic design decision of these smart pointer is that the managed object need not be modified to use a smart pointer; hence they can be used on existing data type (incl. fundamental types).
Importance of weak copies of a shared owning manager
The fundamental property of shared_ptr would create an issue: as every layer of code (that might need to call a function that needs an owning pointer) needs to keep a copy of the shared_ptr around, this can create a web of owning smart pointers, some of which might reside in an object whose lifetime is managed by another who lifetime is managed by that exact smart pointer; because the smart pointer basic specification is that the managed object is not destructed before all copies of the smart pointer in charge of its destruction are destroyed, these objects would never be destroyed (as specified; this is not a consequence of the particular implementation choice of reference counting). Sometimes a copy of a owning smart pointer of a specie that doesn't prevent influence the lifetime of the managed object is needed, hence the need for the weak smart pointer.
A (non null) weak smart pointer is always directly or indirectly a copy of an owning smart pointer, directly or indirectly a copy the original smart pointer that took ownership. That "weak reference" actually is a "strong" owning smart pointer to the information regarding the existence of other owning copies of the smart pointer: as long as there is a weak smart pointer, it will be possible to determine whether there is a live owning smart pointer, and if so to obtain a copy, that is make a shared smart pointer that an exact copy of the original (the lifetime of the original may have ended many generations of copies ago).
The only purpose of a weak smart pointer is to obtain such copies of the original.
The purpose of std::enable_shared_from_this
The only use of std::enable_shared_from_this is to obtain a copy of the original shared_ptr; that implies that such owning smart pointer must already exist. No new original (another smart pointer taking ownership) will be made.
Only use std::enable_shared_from_this for classes that are only intended to be managed by a shared_ptr.
Details of std::enable_shared_from_this
All that being said about the theoretical principles, it's useful to understand what std::enable_shared_from_this contains, how it can produce a shared_ptr when used correctly (and why it cannot be expected to work in any other case).
The "magic" of std::enable_shared_from_this may seem mysterious, and too magic so that users don't have to think about it, but it's actually extremely simple: it keeps a weak_ptr intended to be a copy of the original. Obviously it cannot be constructed as such copy, as the original cannot even be initialized when the std::enable_shared_from_this subobject is constructed: a valid owning smart pointer can only refer to a fully constructed object, because it owns it and is in charge of its destruction. [Even if by some cheating an owning smart pointer was made before the managed object was fully constructed, and hence destructible, the owning smart pointer would be a risk of premature destruction (even if during the normal course of events its lifetime is long, it could be shortened by an exception for example).]
So data member initialization in std::enable_shared_from_this is inherently default initialization: the "weak pointer" is null at that point.
Only when the original finally takes ownership if the managed object, it can collude with std::enable_shared_from_this: the constructing of the original shared_ptr will set once and for all the weak_ptr member inside std::enable_shared_from_this. Active collusion between these components is the only way to make the stuff work.
It's still the user's responsibility to only call shared_from_this only when it can possibly return a copy of the original, that is, after the original has been constructed.
About fake (non owning) owning smart pointers
A fake owning smart pointer is one that does no cleanup ever: owning smart pointer in name only. They are special case of "owning" smart pointers used in such a way that no destruction or cleanup is performed. This ostensibly means that they could be used for objects whose lifetime is predetermined (and long enough) and for which there is a need to have a pretend owning smart pointer; unlike a real owning smart pointer, keeping a copy will not extend the lifetime of the object, so that lifetime should better be really long. (Because a copy of a owning smart pointer could be stored in a global variable, the object could still be expected to be alive after a return from main.)
These non owning owners are obviously a contradiction in the terms and rarely safe (but can be proven safe in a few cases).
They rarely solve a legitimate problem (one that isn't the immediate consequence of a very bad design): a shared_ptr in an interface means that the receiver is expecting to be able to extend the lifetime of the managed object.

How does unique pointer ensure uniqueness?

std::unique_ptr is a smart pointer that retains sole ownership of an object through a pointer and destroys that object when the unique_ptr goes out of scope. No two unique_ptr instances can manage the same object.
How the last statement is ensured?
I don't believe that there is "someone" is STL who checks if one of the already existing std::unique_ptrs already own the raw pointer. This would be very inefficient with huge number of unique pointers, even if it is a linear complexity algorithm. There should be a nice trick, right?
It isn't ensured. The name is a statement of intended usage, not any guarantee fully enforced by a runtime system. That is, you can write this code:
std::unique_ptr<int> i1(new int());
std::unique_ptr<int> i2(i1.get());
and you have two unique_ptrs referring to the same object, but the program has undefined behavior because it will delete the pointer twice.
unique_ptr is not copyable to make it harder to create two such pointers by accident. C++ protects against Murphy, not Machiavelli.
The reason it's called unique is because you can't copy a unique pointer. You can steal its value, but that leaves the original unique_ptr empty.

singleton pattern and std::unique_ptr

std::unique_ptr uniquely controls the object it points to and, hence, does not utilize reference counting. A singleton ensures only one object may be created utilizing reference counting.
Would then std::unique_ptr perform identically to a singleton?
A singleton ensures only one instance of a type.
A unique_ptr ensures only one smart pointer to any instance.
Would then std::unique_ptr perform identically to a singleton?
No. Let's say we have class Foo that's intended to be a singleton. Using a typical singleton pattern, there's no way to construct more than one Foo.
Having a std::unique_ptr<Foo> means there will be one pointer to a particular instance of Foo, but that doesn't prevent the creation of other instances of Foo (either with other unique_ptrs or with raw pointers to local variables). Thus Foo, wouldn't be a singleton.
std::unique_ptr achieves single ownership semantics by only providing a move constructor and no copy constructor or assignment operator.
It is not a case of singleton at all, since you can have multiple unique_ptrs referencing different instances of the same type. A singleton doesn't let you construct the type directly, but provides an accessor that manages a sole instance.
Also, Drew's assertion that
"A unique_ptr ensures only one smart pointer to any instance."
is false. If you simply do:
T* nt = new T;
std::unique_ptr<T> up1(nt);
std::unique_ptr<T> up2(nt);
then you have two unique pointers owning the same resource - and you will only notice a problem at run time, not compile time. Of course, this is incorrect usage of unique_ptr, but this reinforces that a unique_ptr does not ensure you anything, it is simply a pointer container that holds sole ownership from its own perspective, and through its api, makes it hard to accidentally create temporary copies.
Furthermore, you can have other (smart) pointer types pointing to the same raw pointer/resource independently of any unique_ptr. It is completely up to the using code to define ownership and lifetime policies of its resources and smart pointer instances
Correct me if I'm wrong, but as far as I remember a singelton is a class which can only have one instance. That's completely different. So no.

Semantic meanings of std::auto_ptr and boost::shared_ptr

In our large project we have a lot class with the following typedef's:
class Foo
{
public:
typedef std::auto_ptr<Foo> Ptr;
typedef boost::shared_ptr<Foo> Ref;
...
};
...
Foo::Ref foo(new Foo);
...
doBar(foo);
...
The using of them is very convenient. But I doubt if auto_ptr is semantically close to Ptr and shared_ptr is the same as ref? Or should auto_ptr be used explicitly since it has "ownership transfer" semantics?
Thanks,
std::auto_ptr has ownership transfer semantics, but it's quite broken. If you can use boost::shared_ptr, then you should use boost::unique_ptr instead of std::auto_ptr, since it does what one would expect. It transfers ownership and makes the previous instance invalid, which std::auto_ptr doesn't.
Even better, if you can use C++11, then swap to std::unique_ptr and std::shared_ptr.
You shouldn't use std::auto_ptr, its deprecated and I consider it dangerous, even more so when you hide it behind such a generic typedef as Ptr.
I don't think it makes any sense to called shared_ptrRef, in this case it is more Ptr than auto_ptr.
EDIT: I consider it dangerous because you can easily misuse it, even when you fully understand its workings, you can accidentally misuse it, especially when hiding it behind a typedef. A good class should be easy to use right and should be difficult to misuse. Especially with the advent of unique_ptr I can't see any useful scenario for auto_ptr.
I believe the order is just a nomenclature which someone used.
It probably should have been, ref for auto_ptr and ptr for shared_ptr, because:
References are immutable and hence cannot be made to refer to other object. auto_ptr has a similar(albeit remotely similar) semantics, transfer of ownership which means you would probably not want to assign a auto_ptr for the non-intuitive behavior it shows. The assigned object gains ownership while the object being assigned loses ownership.
On the Other hand shared_ptr has an reference counting mechanism which is similar(again remotely) to multiple pointers which can point to the same object. The ownership of the pointer rests with the shared_ptr itself and it gets deallocated as soon as there are no pointer instances referring to it.
A lot depends on what they are being used for. And in the case of
Ref, what people understand by it. In pre-standard days, I would
often use a typedef to Ptr for my (invasive) reference counted
pointer; the presence of such a typedef was, in fact, an indication that
the type supported reference counting, and that it should always be
dynamically allocated.
Both std::auto_ptr and boost::shared_ptr have very special
semantics. I'd tend not to use typedefs for them, both because of the
special semantics, and because (unlike the case of my invasive reference
counted pointer) they are totally independent of the type pointed to.
For any given type, you can use them or not, as the program logic
requires. (Because of its particular semantics, I find a fair number of
uses for std::auto_ptr. I tend to avoid boost::shared_ptr, however;
it's rather dangerous.)
auto_ptr is deprecated in C++11. You might want to stop using it and just use the shared_ptr. For shared_ptr, there is no ownership transfer on assignment, the number of references to the object is counted and the object is destroyed when the last pointer is destroyed.

Should I use shared_ptr or unique_ptr

I've been making some objects using the pimpl idiom, but I'm not sure whether to use std::shared_ptr or std::unique_ptr.
I understand that std::unique_ptr is more efficient, but this isn't so much of an issue for me, as these objects are relatively heavyweight anyway so the cost of std::shared_ptr over std::unique_ptr is relatively minor.
I'm currently going with std::shared_ptr just because of the extra flexibility. For example, using a std::shared_ptr allows me to store these objects in a hashmap for quick access while still being able to return copies of these objects to callers (as I believe any iterators or references may quickly become invalid).
However, these objects in a way really aren't being copied, as changes affect all copies, so I was wondering that perhaps using std::shared_ptr and allowing copies is some sort of anti-pattern or bad thing.
Is this correct?
I've been making some objects using the pimpl idiom, but I'm not sure whether to used shared_ptr or unique_ptr.
Definitely unique_ptr or scoped_ptr.
Pimpl is not a pattern, but an idiom, which deals with compile-time dependency and binary compatibility. It should not affect the semantics of the objects, especially with regard to its copying behavior.
You may use whatever kind of smart pointer you want under the hood, but those 2 guarantee that you won't accidentally share the implementation between two distinct objects, as they require a conscious decision about the implementation of the copy constructor and assignment operator.
However, these objects in a way really aren't being copied, as changes affect all copies, so I was wondering that perhaps using shared_ptr and allowing copies is some sort of anti-pattern or bad thing.
It is not an anti-pattern, in fact, it is a pattern: Aliasing. You already use it, in C++, with bare pointers and references. shared_ptr offer an extra measure of "safety" to avoid dead references, at the cost of extra complexity and new issues (beware of cycles which create memory leaks).
Unrelated to Pimpl
I understand unique_ptr is more efficient, but this isn't so much of an issue for me, as these objects are relatively heavyweight anyway so the cost of shared_ptr over unique_ptr is relatively minor.
If you can factor out some state, you may want to take a look at the Flyweight pattern.
If you use shared_ptr, it's not really the classical pimpl
idiom (unless you take additional steps). But the real question
is why you want to use a smart pointer to begin with; it's very
clear where the delete should occur, and there's no issue of
exception safety or other to be concerned with. At most,
a smart pointer will save you a line or two of code. And the
only one which has the correct semantics is boost::scoped_ptr,
and I don't think it works in this case. (IIRC, it requires
a complete type in order to be instantiated, but I could be
wrong.)
An important aspect of the pimpl idiom is that its use should be
transparent to the client; the class should behave exactly as if
it were implemented classically. This means either inhibiting
copy and assignment or implementing deep copy, unless the class
is immutable (no non-const member functions). None of the usual
smart pointers implement deep copy; you could implement one, of
course, but it would probably still require a complete type
whenever the copy occurs, which means that you'd still have to
provide a user defined copy constructor and assignment operator
(since they can't be inline). Given this, it's probably not
worth the bother using the smart pointer.
An exception is if the objects are immutable. In this case, it
doesn't matter whether the copy is deep or not, and shared_ptr
handles the situation completely.
When you use a shared_ptr (for example in a container, then look this up and return it by-value), you are not causing a copy of the object it points to, simply a copy of the pointer with a reference count.
This means that if you modify the underlying object from multiple points, then you affect changes on the same instance. This is exactly what it is designed for, so not some anti-pattern!
When passing a shared_ptr (as the comments say,) it's better to pass by const reference and copy (there by incrementing the reference count) where needed. As for return, case-by-case.
Yes, please use them. Simply put, the shared_ptr is an implementation of smart pointer. unique_ptr is an implementation of automatic pointer: