A Smart Pointer is a wrapper class over a pointer with an operator like * and -> overloaded. The objects of the smart pointer class look like normal pointers. But, unlike Normal Pointers it can deallocate and free destroyed object memory.
std::unique_ptr has no memory or performance overhead compared to the explicit usage of new and delete. That is very great because std::unique_ptr offers a great benefit by automatically managing the lifetime of its resource without any additional cost. My conclusion to std::shared_ptr is not so easy.
Smart pointers try to prevent memory leaks by making the resource deallocation automatic: when the pointer to an object (or the last in a series of pointers) is destroyed, for example because it goes out of scope, the pointed object is destroyed too.
One of the advantages of smart pointers is, that they ensure due to RAII, that the actual object is deleted. When using a raw pointer, you need to have a delete for every possible exit point, and still an exception will lead to a memory leak. Smart pointers will also free the memory if an exception occurs.
std::unique_ptr
has memory overhead only if you provide it with some non-trivial deleter.
std::shared_ptr
always has memory overhead for reference counter, though it is very small.
std::unique_ptr
has time overhead only during constructor (if it has to copy the provided deleter and/or null-initialize the pointer) and during destructor (to destroy the owned object).
std::shared_ptr
has time overhead in constructor (to create the reference counter), in destructor (to decrement the reference counter and possibly destroy the object) and in assignment operator (to increment the reference counter). Due to thread-safety guarantees of std::shared_ptr
, these increments/decrements are atomic, thus adding some more overhead.
Note that none of them has time overhead in dereferencing (in getting the reference to owned object), while this operation seems to be the most common for pointers.
To sum up, there is some overhead, but it shouldn't make the code slow unless you continuously create and destroy smart pointers.
My answer is different from the others and i really wonder if they ever profiled code.
shared_ptr has a significant overhead for creation because of it's memory allocation for the control block (which keeps the ref counter and a pointer list to all weak references). It has also a huge memory overhead because of this and the fact that std::shared_ptr is always a 2 pointer tuple (one to the object, one to the control block).
If you pass a shared_pointer to a function as a value parameter then it will be at least 10 times slower then a normal call and create lots of codes in the code segment for the stack unwinding. If you pass it by reference you get an additional indirection which can be also pretty worse in terms of performance.
Thats why you should not do this unless the function is really involved in ownership management. Otherwise use "shared_ptr.get()". It is not designed to make sure your object isn't killed during a normal function call.
If you go mad and use shared_ptr on small objects like an abstract syntax tree in a compiler or on small nodes in any other graph structure you will see a huge perfomance drop and a huge memory increase. I have seen a parser system which was rewritten soon after C++14 hit the market and before the programmer learned to use smart pointers correctly. The rewrite was a magnitude slower then the old code.
It is not a silver bullet and raw pointers aren't bad by definition either. Bad programmers are bad and bad design is bad. Design with care, design with clear ownership in mind and try to use the shared_ptr mostly on the subsystem API boundary.
If you want to learn more you can watch Nicolai M. Josuttis good talk about "The Real Price of Shared Pointers in C++" https://vimeo.com/131189627
It goes deep into the implementation details and CPU architecture for write barriers, atomic locks etc. once listening you will never talk about this feature being cheap. If you just want a proof of the magnitude slower, skip the first 48 minutes and watch him running example code which runs upto 180 times slower (compiled with -O3) when using shared pointer everywhere.
EDITED:
And if you ask about "std::unique_ptr" than visit this talk "CppCon 2019: Chandler Carruth “There Are No Zero-cost Abstractions” https://www.youtube.com/watch?v=rHIkrotSwcc
Its just not true, that unique_ptr is 100% cost free.
OFFTOPIC:
I tried to educate people about the the false idea that using exceptions that are not thrown has no cost penalty for over two decades now. In this case it's in the optimizer and the code size.
As with all code performance, the only really reliable means to obtain hard information is to measure and/or inspect machine code.
That said, simple reasoning says that
You can expect some overhead in debug builds, since e.g. operator->
must be executed as a function call so that you can step into it (this is in turn due to general lack of support for marking classes and functions as non-debug).
For shared_ptr
you can expect some overhead in initial creation, since that involves dynamic allocation of a control block, and dynamic allocation is very much slower than any other basic operation in C++ (do use make_shared
when practically possible, to minimize that overhead).
Also for shared_ptr
there is some minimal overhead in maintaining a reference count, e.g. when passing a shared_ptr
by value, but there's no such overhead for unique_ptr
.
Keeping the first point above in mind, when you measure, do that both for debug and release builds.
The international C++ standardization committee has published a technical report on performance, but this was in 2006, before unique_ptr
and shared_ptr
were added to the standard library. Still, smart pointers were old hat at that point, so the report considered also that. Quoting the relevant part:
“if accessing a value through a trivial smart pointer is significantly slower than accessing it through an ordinary pointer, the compiler is inefficiently handling the abstraction. In the past, most compilers had significant abstraction penalties and several current compilers still do. However, at least two compilers have been reported to have abstraction penalties below 1% and another a penalty of 3%, so eliminating this kind of overhead is well within the state of the art”
As an informed guess, the “well within the state of the art” has been achieved with the most popular compilers today, as of early 2014.
In other words, is my code going to be slower if I use smart pointers, and if so, how much slower?
Slower? Most likely not, unless you are creating a huge index using shared_ptrs and you have not enough memory to the point that your computer starts wrinkling, like an old lady being plummeted to the ground by an unbearable force from afar.
What would make your code slower is sluggish searches, unnecessary loop processing, huge copies of data, and a lot of write operations to disk (like hundreds).
The advantages of a smart pointer are all related to management. But is the overhead necessary? This depends on your implementation. Let's say you are iterating over an array of 3 phases, each phase has an array of 1024 elements. Creating a smart_ptr
for this process might be overkill, since once the iteration is done you'll know you have to erase it. So you could gain extra memory from not using a smart_ptr
...
A single memory leak could make your product have a point of failure in time (let's say your program leaks 4 megabytes each hour, it would take months to break a computer, nevertheless, it will break, you know it because the leak is there).
Is like saying "you software is guaranteed for 3 months, then, call me for service."
So in the end it really is a matter of... can you handle this risk? does using a raw pointer to handle your indexing over hundreds of different objects is worth loosing control of the memory.
If the answer is yes, then use a raw pointer.
If you don't even want to consider it, a smart_ptr
is a good, viable, and awesome solution.
Chandler Carruth has a few surprising "discoveries" on unique_ptr
in his 2019 Cppcon talk. (Youtube). I can't explain it quite as well.
I hope I understood the two main points right:
unique_ptr
will (often incorrectly) not handle cases where owership is not passed while passing a pointer. Rewriting it to use unique_ptr
will add that handling, and that has some overhead.unique_ptr
is still a C++ object, and objects will be passed on stack when calling a function, unlike pointers, which can be passed in registers.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With