Does enable_shared_from_this and make_shared provide the same optimization - c++

As I understand make_shared<T>(...) may provide some memory allocation optimization (it may allocate reference counter within same memory block as instance of class T).
Do enable_shared_from_this provides the same optimization? So:
class T : std::enable_shared_from_this<T> {};
...
auto t = std::shared_ptr<T>(new T);
Is the same as:
class T {};
...
auto t = std::make_shared<T>();
If not take in account sizeof(T).

Do enable_shared_from_this provides the same optimization? So:
No. As you can see from the wording in the standard, enable_shared_from_this<T> has a weak_ptr<T> data member. That adds a weak_ptr<T> to the class, which has a pointer to the control block that contains the reference counts. It doesn't contain the reference counts directly. The control block containing the reference counts still exists external to the object.
The control block containing the reference counts must outlive the object, so that other weak_ptr objects that used to refer to the object can still access the control block, to check whether it has expired.
If the control block was inside the object it would be destroyed when the object was destroyed, and it would not be possible for a dangling weak_ptr to safely determine if the object had expired. In theory the memory of the control block could remain allocated and still be used and the reference counts updated, even though the object they were part of was destroyed, but that seems pretty ugly (and it would mean the object would not be destroyed with delete, it would require an explicit destructor call and explicit operator delete call to free the memory).
You also couldn't use the embedded control block if the owning shared_ptr was created with a custom deleter or custom allocator, because the size of those objects would not be known in advance. In such cases you'd still need to allocate an external control block in addition to the one embeded in the enable_shared_from_this<T> base class, wasting even more space.

Related

std::make_shared(), std::weak_ptr and cyclic references

My question is about this claim:
If any std::weak_ptr references the control block created by std::make_shared after the lifetime of all shared owners ended, the memory occupied by T persists until all weak owners get destroyed as well, which may be undesirable if sizeof(T) is large. Source
I read here, that this object live until last weak_ptr is present.
Does it free object made with make_shared, with cyclic reference to self or it will live in memory forever?
For example:
struct A
{
std::weak_ptr<A> parent;
}
void fn()
{
auto a=std::make_shared<A>();
a->parent = a;
} // Will it destroy here or not?
It is destroyed. That's one of the reason why weak_ptr exists.
When a is destroyed, the reference counter becomes 0, so the object is destroyed. That means the destructor of the object is called, which destroys a->parent too.
Don't confuse destruction with deallocation. When reference counter becomes 0, or no shared_ptr owns the object, the object is destroyed. If there is any weak_ptr which points the control block, the memory won't be deallocated - because the object was allocated with std::make_shared - but the object is definitely destroyed.
Problem involve with:
If any std::weak_ptr references the control block created by std::make_shared after the lifetime of all shared owners ended, the memory occupied by T persists until all weak owners get destroyed as well, which may be undesirable if sizeof(T) is large
is something like
std::weak_ptr<A> global;
void foo()
{
auto a = std::make_shared<A>();
global = a;
}
So global point to control block of a.
Here even if a is destroyed, the memory used by a is still present to allow the control block to exist.
There is a cycle of object reachability via implementation pointers, that is, you can follow pointers used inside of the private implementations of these objects and go back to where you started: a->parent contains a pointer to the meta information (control block) created either by std::shared_ptr or by std::make_shared.
Of course std::make_shared is expected to minimize the number of dynamic memory allocations by putting together the meta information and the managed object, but that has no bearing on when the managed object is destroyed (which is the only observable aspect since no class specific operator new/operator delete was used). So whether the control block is collocated with the managed object, or has a pointer to that object allocated separately, is irrelevant.
In all but a handful of degenerate cases (where the smart pointer is constructed with a fake deleter that doesn't deallocate anything), the end of the lifetime of the last shared owning smart pointer causes a deleter to run, usually:
of the form delete p; to run, where p is the argument of type T* of the constructor of std::shared_ptr<U>,
or of the form p->~T(), where p is the result of new (buff) T in std::make_shared<T>().
In either case, the value of p can be obtained from the meta information.
[Note that the value p to be deleted is never obtained from the U* pointer value stored in any particular std::shared_ptr<U> instance, as such pointer value may not be "deletable", as the destructor may not be virtual (as long as the pointer argument std::shared_ptr<U> has the right static type: it's sufficient that delete p; where p is the value of the type passed to the templated constructor), as the pointer may be to a member subobject or a complete different complete object, if an aliasing constructor was used to construct another std::shared_ptr with shared ownership.]
So having a pointer to the control block usually allows one to recover a pointer to the controlled object, although that pointer to the complete object cannot be obtained through the public interface (except that the pointer is passed to the deleter, so the only way to recover the pointer in C++, if it was lost, would be to have passed a custom deleter and wait for it to be called). The pointer can certainly be recovered by navigating inside the memory representation (although that navigation might need to use dynamic_cast to a type unknown at compile time, something the debugger will be able to do as long as it knows about all derived classes).
So we have the cycle:
a
a->parent
parent->control_block
control_block.deleter (virtual call or stored function)
deleter.a
if the pointer is stored in a dynamically created deleter, as is necessary to create std::shared_ptr<U>(T*), or
a
a->parent
parent->control_block
control_block.buffer
for objects created with a single allocation make_shared: the object was constructed inside that buffer so &control_block.buffer == a.
But cycles of pointers are not an issue, only cycle of ownership as it implies "self ownership controlled by lifetime", that is "I will destruct myself only when my lifetime will be over" (aka "I will enter destructor when I will have entered destructor"), an absurdity.
Here there is no ownership as a weak reference only owns the meta information, not the information.

Why are two raw pointers to the managed object needed in std::shared_ptr implementation?

Here's a quote from cppreference's implementation note section of std::shared_ptr, which mentions that there are two different pointers(as shown in bold) : the one that can be returned by get(), and the one holding the actual data within the control block.
In a typical implementation, std::shared_ptr holds only two pointers:
the stored pointer (one returned by get())
a pointer to control block
The control block is a dynamically-allocated object that holds:
either a pointer to the managed object or the managed object itself
the deleter (type-erased)
the allocator (type-erased)
the number of shared_ptrs that own the managed object
the number of weak_ptrs that refer to the managed object
The pointer held by the shared_ptr directly is the one returned by get(), while the pointer or object held by the control block is the one that will be deleted when the number of shared owners reaches zero. These pointers are not necessarily equal.
My question is, why are two different pointer(the two in bold) needed for the managed object (in addition to the pointer to the control block)? Doesn't the one returned by get() suffice? And why aren't these pointers necessarily equal?
The reason for this is that you can have a shared_ptr which points to something else than what it owns, and that is by design. This is implemented using the constructor listed as nr. 8 on cppreference:
template< class Y >
shared_ptr( const shared_ptr<Y>& r, T *ptr );
A shared_ptr created with this constructor shares ownership with r, but points to ptr. Consider this (contrived, but illustrating) code:
std::shared_ptr<int> creator()
{
using Pair = std::pair<int, double>;
std::shared_ptr<Pair> p(new Pair(42, 3.14));
std::shared_ptr<int> q(p, &(p->first));
return q;
}
Once this function exits, only a pointer to the int subobject of the pair is available to client code. But because of the shared ownership between q and p, the pointer q keeps the entire Pair object alive.
Once dealloacation is supposed to happen, the pointer to the entire Pair object must be passed to the deleter. Hence the pointer to the Pair object must be stored somewhere alongside the deleter—in other words, in the control block.
For a less contrived example (probably even one closer to the original motivation for the feature), consider the case of pointing to a base class. Something like this:
struct Base1
{
// :::
};
struct Base2
{
// :::
};
struct Derived : Base1, Base2
{
// :::
};
std::shared_ptr<Base2> creator()
{
std::shared_ptr<Derived> p(new Derived());
std::shared_ptr<Base2> q(p, static_cast<Base2*>(p.get()));
return q;
}
Of course, the real implementation of std::shared_ptr has all the implicit conversions in place so that the p-and-q dance in creator is not necessary, but I've kept it there to resemble the first example.
Additional link to #Angew 's answer:
Peter Dimov, Beman Dawes and Greg Colvin proposed shared_ptr and weak_ptr for inclusion in the Standard Library via the first Library Technical Report (known as TR1). The proposal was accepted and eventually went on to become a part of the C++ standard in its 2011 iteration.
boost smart pointer history
In this proposal, the authors pointed out that the usage of "Shared Pointer Aliasing":
Advanced users often require the ability to create a shared_ptr instance p that shares ownership with another (master) shared_ptr q but points to an object that is not a base of *q. *p may be a member or an element of *q, for example. This section proposes an additional constructor that can be used for this purpose.
So they add an additional pointer into the control block.
One inescapable need for a control block is to support weak pointers. It is not always feasible to notify all weak pointers on destruction of an object (in fact, it is almost always infeasible). Accordingly, the weak pointers need something to point at until they have all gone away. Thus, some block of memory has to hang around. That block of memory is the control block. Sometimes they may be allocated together, but allocating them separately allows you to reclaim a potentially expensive object while keeping around the cheap control block.
The general rule is that the control block persists as long as there exists a single shared pointer or weak pointer referring to it, while the object is allowed to be reclaimed the instant there are no shared pointers pointing at it.
This also allows for cases where the object is brought into shared ownership after its allocation. make_shared may be able to bundle these two concepts into one block of memory, but shared_ptr<T>(new T) must first allocate T, and then figure out how to share it after the fact. When this is undesirable, boost has a related concept of intrusive_ptr which does its reference counting directly inside the object rather than with a control block (you have to write increment and decrement operators yourself to make this work).
I have seen shared pointer implementations which do not have a control block. Instead, the shared pointers develop a linked-list between themselves. As long as the linked-list contains 1 or more shared_ptrs, the object is still alive. However, this approach is more complicated in a multithreading scenario because you have to maintain the linked list rather than just a simple ref count. Its runtime is also likely to be worse in many scenarios where you are assigning and re-assigning shared_ptrs repeatedly, because the linked-list is more heavy-weight.
It is also possible for a high-performance implementation to pool allocate the control blocks, driving the cost of using them to nearly zero.
Let look at a std::shared_ptr<int>
This is a reference counted smart pointer to an int*. Now the int* holds no reference counting information and the shared_ptr object itself cannot hold the reference counting information since it may well be destructed well before the reference count drops down to zero.
This means that we must have an intermediate object to hold the control information that is guaranteed to remain persistent until the reference count drops to zero.
Having said that,if you create shared_ptr with make_shared both the int and the control block will be created in contiguous memory making dereferencing much more efficient.

Syntax for converting expired weak_ptr<T> to shared_ptr<T>

From what I've read, a shared_ptr<T> does not get de-allocated until both strong references AND weak references to it are dropped.
I understand a shared object to be considered expired when there are no more strong references to it. The standard lock() function of a weak_ptr<T> therefore fails in such a case because the object is considered 'expired'.
However, if the deleter of a shared pointer is overridden such that the managed object is not deleted, then it should be valid to generated shared_ptr<T> from a weak_ptr<T> - but I cannot find the right syntax to do this.
std::shared_ptr<int> s_ptr(new(42), D());
std::weak_ptr<int) w_ptr(s_ptr);
s_ptr.reset();
s_ptr = std::shared_ptr<int>(w_ptr, false);
EDIT
To clarify this a bit further, I'm trying to construct an object pool of re-usable shared_ptr<T>. The reason behind this is because every use of shared_ptr results in one or more heap memory allocations. So I've added a deleter to every shared_ptr<T> which stores a weak_ptr<T> reference such that then the deleter gets called it should be able to re-add itself to a pool of available shared_ptr<T> objects (with managed object intact). By keeping a weak_ptr<T> stored inside the shared_ptr<T>'s deleter, it therefore shouldn't stop the deleter from being called.
The end goal is to obtain a smart pointer that doesn't do consisant heap allocation - or at least only a small number.
From what I've read, a shared_ptr does not get de-allocated until both strong references AND weak references to it are dropped.
Wrong. a std::shared_ptr has two blocks - a control block that contains reference counts, etc, then another block for the actual data.
When the shared count goes to 0, the data block is (usually) deallocated. This is why it is illegal to make a std::shared_ptr from a expired std::weak_ptr.
On another note, why would you EVER want this functionality? It ruins the entire point of std::weak_ptr, which is to be able "store" a pointer to a object stored by std::shared_ptr without incrementing its reference count.
If you want to do that, then please just use a std::shared_ptr
If pooling control blocks is a good idea, the library implementation may already be doing it. In fact, the implementation of new itself may already be doing pooling to support similar memory usage patterns.
Furthermore, you can achieve what I think your goals are by using make_shared rather than invoking new and passing it into a shared_ptr constructor; one of the reasons for the existence of this helper function is that it can be written to use a single allocation to allocate both the control block and the new object you're creating at the same time.

Do std::weak_ptrs affect when the memory allocated by std::make_shared is deallocated?

If I call std::make_shared<T> (rather than just allocating a shared_ptr<T> explicitly) then I expect the reference count to be allocated in memory alongside the instance of T, for performance reasons. All well and good.
But if I have weak_ptr instances referencing the same object, presumably they will need access to that reference count, to know whether the object still exists.
So, when the last shared_ptr to the instance of T is destroyed, a naive understanding of the system would imply that it cannot deallocate the memory that T is stored in, because weak_ptrs still require access to that count.
It seems like there is a separate weak reference counter and in theory that could be held separately from the instance of T, so that the T can be destroyed and the memory deallocated while weak references still exist. But then we're back to having 2 separate allocations, thwarting the benefits of make_shared.
I assume I am misunderstanding something here. How can the memory allocated for a instance constructed via std::make_shared be freed when weak references exist?
If you use make_shared and if the implementation uses a single allocation for both the object and the reference counts, then that allocation cannot be freed until all references (both strong and weak) have been released.
However, the object will be destroyed after all strong references have been released (regardless of whether there are still weak references).
The common implementation is for the ref control block of an std::shared_ptr to contain both a strong and a weak reference count separately. The managed object is destroyed when the strong reference count goes to zero, but the ref control block itself is only released when the weak reference count also reaches zero.
(When you use std::make_shared, the ref control block itself contains enough memory to hold the managed object. This is just a detail.)
In other words, the observable behaviour for the managed object is independent of weak pointers.

Will the smart pointer or scoped pointers delete an object when the class has no destructor

Will the smart pointer or scoped pointers delete an object when the class has no destructor
If not, why not just leave the scope and let the object be deleted by itself?
All class members are deleted even if you don't have a destructor when the instance is deleted. Memory leaks occur when you deal with pointers:
class A
{
private:
B* b;
};
In this case, b itself will be destroyed when the instance of A is deleted, but the memory it points to will not.
class A
{
private:
SmartPtr<B> b;
};
In the case of smart pointers, which usually have some reference counting and memory cleanup in the destructor, the memory it points to will be explicitly destroyed by its destructor, and the destructor of the smart pointer will be implicitly called when the instance of the containing class is destroyed.
yes. that s what smart pointers are used for.
http://www.boost.org/doc/libs/1_48_0/libs/smart_ptr/smart_ptr.htm
http://en.wikipedia.org/wiki/Smart_pointer
What is a smart pointer and when should I use one?
Yes, smart pointer deletes the object irrespective of whether class has destructor or not. Note that smart pointers are used with objects allocated on heap (using new) and these object won't release memory when they go out of scope, you need to explicitly delete them. Smart pointers will remove this process of explicitly deleting them.
The pointer to the object itself will be deleted. However if there is any dynamically allocated data in the class it will not get freed. The idea of having a destructor is to be able to post-process the class object and mainly - free any resources taken.
new and delete do two things.
new allocates memory and gets an object to construct itself in that memory space.
delete first gets the object to destruct itself then releases the memory.
Note that some smart pointers can be given a custom deleter which doesn't call delete on the object but whatever you ask it to do. There may be occasions when you wish to pass it a no-op.
Your point is well taken; there aren't that many uses for smart pointers
in C++, since most of the time where they might be relevant, you'd be
better off using value semantics, and copying. In the case of
scoped_ptr, the most frequent use is when the actual object is
polymorphic:
scoped_ptr<Base> pObj = condition
? static_cast<Base*>( new Derived1 )
: static_cast<Base*>( new Derived2 );
(Regretfully, at least one of the static_cast is necessary.)
If you are dealing with a container of polymorphic objects, you'll need
shared_ptr instead, and if you're returning a polymorphic object, or
otherwise passing it around, you will use unique_ptr if you can
guarantee its availability, and auto_ptr otherwise—in some cases
where you're passing it around, shared_ptr might be more appropriate.
In the case of returning an object or passing it around, the cost of
copying it might also be a motive for using a smart pointer, even if the
object isn't polymorphic. In such cases, I'd still use value semantics
(i.e. copying and assigning the object itself) unless I had a
performance problem.
Note that smart pointers aren't only used for memory management. I
regularly use auto_ptr in the interface of queues between threads:
once the object has been inserted into the queue, it no longer belongs
to the sending thread; auto_ptr expresses these semantics exactly,
with the auto_ptr in the sending thread becoming invalid. Or a
modifiable singleton (something which should be very, very rare) might
acquire a lock in its instance function, and return a shared_ptr
which frees the lock in its final destructor. I've also used smart
pointers in one or two cases to ensure transactional semantics: in one
case, for example, the objects were in a number of different sets (which
held pointers to them, of course), ordered according to different
criteria. To modify an object, you acquired a shared pointer to it; the
function which returned this shared pointer also removed the objects
from the sets, so that you could safely modify the key values as well,
and the final destructor reinserted them into the sets according to the
new keys. And so on—there are any number of uses for smart
pointers that have nothing to do with memory management or object
lifetime.