C++ Linked list using smart pointers - c++

I have only been using raw pointers for linked list with templates. For example, the member data, Node<T>* head; and when I am inserting a node one of the lines would be head = new Node<T>(data);.
However, now I need to use a smart pointer and I am not sure how I would change it to use smart pointers. Would the member data be changed to shared_ptr<Node<T>> head; and the other line would change to
head = shared_ptr<Node<T>>( new <Node<T>>(data) );?

You do not "need" to use a smart pointer for a linked list, because that statement doesn't make sense. You do not use smart pointers for low-level data structures. You use smart pointers for high-level program logic.
As far as low-level data structures are concerned, you use a standard container class from the C++ standard library, like std::list [*], which solves all your memory-management problems anyway, without using any smart pointers internally.
If you really really need your own highly specialised/optimised custom container class because the entire C++ standard library is unfit for your requirements and you need a replacement for std::list, std::vector, std::unordered_map and other optimised, tested, documented and safe containers – which I very much doubt! –, then you have to manage memory manually anyway, because the point of such a specialised class will almost certainly be the need for techniques like memory pools, copy-on-write or even garbage collection, all of which conflict with a typical smart pointer's rather simplistic deletion logic.
In the words of Herb Sutter:
Never use owning raw pointers and delete, except in rare cases when
implementing your own low-level data structure (and even then keep
that well encapsulated inside a class boundary).
Something along those lines is also expressed in Herb Sutter's and Bjarne Stroustrup's C++ Core Guidelines:
This problem cannot be solved (at scale) by transforming all owning
pointers to unique_ptrs and shared_ptrs, partly because we need/use
owning "raw pointers" as well as simple pointers in the implementation
of our fundamental resource handles. For example, common vector
implementations have one owning pointer and two non-owning pointers.
Writing a linked-list class in C++ with raw pointers can be a useful academic exercise. Writing a linked-list class in C++ with smart pointers is a pointless academic exercise. Using any of these two self-made things in production code is almost automatically wrong.
[*] Or just std::vector, because due to cache locality that will almost always be the better choice anyway.

There are basically two alternatives to set up a smart-pointer enhanced list:
Using std::unique_ptr:
template<typename T>
struct Node
{
Node* _prev;
std::unique_ptr<Node> _next;
T data;
};
std::unique_ptr<Node<T> > root; //inside list
That would be my first choice. The unique-pointer _next takes care there are no memory leaks, whereas _prev is an observing pointer. However, copy constructor and such things -- in case you need them -- need to be defined and implemented by hand.
Using shared_ptr:
template<typename T>
struct Node
{
std::weak_ptr<Node> _prev; //or as well Node*
std::shared_ptr<Node> _next;
T data;
};
std::shared_ptr<Node<T> > root; //inside list
This is alternative is copyable by design and adds further safety because of the weak_ptr, see below. It is less performant than the unique_ptr when it comes to structural changes of the list, such as insertions and removals, e.g. due to thread safety in shared_ptr's control block.
Yet, traversing the list, i.e. dereferencing the pointers, should be as performant as for the unique_ptr.
In both approaches the idea is that one node owns the complete remaining list. Now when a node goes out of scope, there is no danger that the remaining list becomes a memory leak, as the nodes are iteratively destructed (starting from the last one).
The _prev pointer is in both options only an observing pointer: it's task is not to keep the previous nodes alive, but only to provide a link to visit them.
For that, a Node * is usually sufficient (--note: observing pointer means you never do memory related stuff like new, delete on the pointer).
If you want more safety, you can also use a std::weak_ptr which prevents from things like
std::shared_ptr<Node<T> > n;
{
list<T> li;
//fill the list
n = li.root->next->next; //let's say that works for this example
}
n->_prev; //dangling pointer, the previous list does not exists anymore
Using a weak_ptr, you can lock() it and in this way chack whether _prev is still valid.

I would look at the interface of std::list, which is a C++ implementation of linked lists. It seems that you are approaching the templating of your Linked list class wrong. Ideally your linked list should not care about ownership semantics (i.e. whether it is instantiated with raw ptrs, smart pointers or stack allocated variables). An example of ownership sematics with STL containers follows. However, there are better examples of STL and ownership from more authoritative sources.
#include <iostream>
#include <list>
#include <memory>
using namespace std;
int main()
{
// Unique ownership.
unique_ptr<int> int_ptr = make_unique<int>(5);
{
// list of uniquely owned integers.
list<unique_ptr<int>> list_unique_integers;
// Transfer of ownership from my parent stack frame to the
// unique_ptr list.
list_unique_integers.push_back(move(int_ptr));
} // list is destroyed and the integers it owns.
// Accessing the integer here is not a good idea.
// cout << *int_ptr << endl;
// You can make a new one though.
int_ptr.reset(new int(6));
// Shared ownership.
// Create a pointer we intend to share.
shared_ptr<int> a_shared_int = make_shared<int>(5);
{
// A list that shares ownership of integers with anyone that has
// copied the shared pointer.
list<shared_ptr<int>> list_shared_integers;
list_shared_integers.push_back(a_shared_int);
// Editing and reading obviously works.
const shared_ptr<int> a_ref_to_int = list_shared_integers.back();
(*a_ref_to_int)++;
cout << *a_ref_to_int << endl;
} // list_shared_integers goes out of scope, but the integer is not as a
// "reference" to it still exists.
// a_shared_int is still accessible.
(*a_shared_int)++;
cout << (*a_shared_int) << endl;
} // now the integer is deallocated because the shared_ptr goes
// out of scope.
A good exercise to understand ownership, memory allocation/deallocation, and shared pointers is to do a tutorial where you implement your own smart pointers. Then you will understand exactly how to use smart pointers and you will have one of those xen moments where you realise how pretty much everything in C++ comes back to RAII (ownership of resources).
So back to the crux of your question. If you want to stick to Nodes of type T, don't wrap the node in a smart pointer. The Node destructor must delete the underlying raw pointer. The raw pointer may point to a smart pointer itself specified as T. When your "LinkedList"'s class destructor is called it iterates through all Nodes with Node::next and calls delete node; after it obtained the pointer to the next node.
You could create a list where nodes are smart pointers... but this is a very specialised linked list probably called SharedLinkedList or UniqueLinkedList with very different sematics for object creation, popping, etc. Just as an example, a UniqueLinkedList would move a node in the return value when popping a value to a caller. To do metaprogramming for this problem would require the use of partial specialization for different types of T passed. Example, something like:
template<class T>
struct LinkedList
{
Node<T> *head;
};
// The very start of a LinkedList with shared ownership. In all your access
// methods, etc... you will be returning copies of the appropriate pointer,
// therefore creating another reference to the underlying data.
template<class T>
struct LinkedList<std::shared_ptr<T>>
{
shared_ptr<Node<T>> head;
};
Now you start implementing your own STL! You can already see potential for problems as mentioned in the comments to your question with this approach. If nodes have shared_ptr next it will result in a call to that shared Node's destructor, which will call the next shared Node destructor and so forth (stack overflow due to the recursion is possible). So that is why I don't care much for this approach.

Structure will look like
template<typename T> struct Node
{
T data;
shared_ptr<Node<T>> next;
};
Creating of node will look like
shared_ptr<Node<int>> head(new Node<int>);
or
auto head = make_shared<Node>(Node{ 1,nullptr });

dont use smart pointer in graph like data structure because it may cause stack overflow an many performance issue due to recursive call of destructor or inc, decr reference count wich it non optimal due to how dfs and bfs algorithm work

Related

How to build, with smart pointers, a tree that survive the end of its scope

I need to build a tree which will be generated in a game loop. This tree has to stay in memory at the end of this loop, to be still available in the next iteration. In consequence, I have to use heap memory. I could use raw pointers and it would probably be fine. However, I read that in modern C++, one should use smart pointers instead; and smart pointers delete themselves when reaching end of scope.
To bypass this problem, I found an implementation where I store the smart pointers in a vector which is globally declared. And it works:
class Node;
vector<std::unique_ptr<Node>>childsList;
class Node
{
public:
Node(int recursionLevel)
{
if (recursionLevel < 2)
{
childsList.emplace_back(std::make_unique<Node>(recursionLevel+1));
childsList.emplace_back(std::make_unique<Node>(recursionLevel+1));
}
}
};
int main()
{
{ // This scope is the game loop. The tree has to survive to the end of this loop iteration.
childsList.emplace_back(std::make_unique<Node>(0)); // Tree creation
}
}
Is it the good way to do it? I feel like I'm doing it dirty, and that smart pointers are not made for this kind of job. Also, performance is important here.
Optional question: I noted that I could use push_back instead of emplace_back with no difference (no unique pointer object created on the stack before to be copied into the vector). Do push_back behave the same as emplace_back for unique_ptr?

Clarifying destructor requirements for data types in a struct in C++

Destructors of structs:
Could you specify every type of data type that would be have to be explicitly handled in the destructor of a struct?
struct Node {
int val; // representing any/all primitive data types
int* ptrToVal; // representing any/all pointers to primitive data types
int arr[5]; // representing any/all arrays
int* ptrToArr[5]; // representing any/all array of pointers
Node* next; // pointer to a struct
vector<Node> vOfNodes; // vector of structs
vector<Node*> vOfPtrs; // vector of struct pointers
unordered_map<int, Node*> um; //representing any pre-existing class template
// Default constructor
Node() : val(0), ptrToVal(nullptr), arr(), ptrToArr(), next(nullptr),
vOfNodes(), vOfPtrs(), um(){}
//Overloaded constructor
Node(int val, int* toVal, Node* n, vector<Node> vN, vector<Node*> toV,
unordered_map<int, Node*> m)
: val(val), ptrToVal(toVal), arr(), ptrToArr(), next(n),
vOfNodes(vN), vOfPtrs(toV), um(m){}
What would be necessary to add to the destructor of the Node struct? Is there any other tricky data structures that I didn’t couldn’t think of that would also necessitate non-trivial code in the destructor?
Anything could require explicit destruction: an int could be a file descriptor to be closed, an index into an array (external to the object) identifying an object to be finalized in some way, or a pointer stored as std::uintptr_t for type erasure reasons. A function pointer could be a registered cleanup function to be called. An object with its own destructor could contain information of any of these types that it doesn’t know how to handle.
On the other hand, a raw object pointer, which is the poster child for explicit destruction, might just be a non-owning (“observing”) pointer into another data structure and need no cleanup at all.
So there’s no predetermined answer: you have to consider why the object has each member and what it owns about each.
What would be necessary to add to the destructor of the Node struct?
Based on the code shown, nothing. Guessing a fair amount, maybe some delete statements. Ideally, nothing.
A destructor is mainly for "undoing" some earlier action associated with an object when that object's lifetime ends - like the corresponding delete to a new, or a file close to an open, or resetting temporary configuration changes back to originals, or etc. But nothing in those members just existing requires any C++ code to undo.
Though some of these members are or contain "ambiguous pointers". We can't tell just by looking how they're to be used: can pointers to any particular objects go there? Only pointers which came from new expressions? If they're from new expressions, is the struct "responsible" for cleaning up with an eventual delete (or delete[]), or does the code which did the new expressions still have responsibility for that? These questions could apply to every pointer involved: ptrToVal, next, and the elements within ptrToArr (misleadingly named), vOfPtrs, and/or um.
If the answer is that the struct is never responsible for doing delete, then the "raw pointer" is an okay choice after all, and the struct doesn't need a destructor declared. (C++ will automatically give it a destructor, and define it if needed.)
If the answer is that the struct should do some delete on some of those pointers, we could define its destructor to do that. But even better, we could change the members from raw pointers to smart pointers, like std::unique_ptr<Node> next; or std::vector<std::shared_ptr<Node>> vOfPtrs;. Smart pointers are one type of RAII handle (Resource Allocation Is Initialization) .
RAII handles in C++:
make it more obvious what the pattern is for cleanup responsibility
save us from nasty and hard to find bugs when new and delete, or other Do/Undo operations, are misused
save us from writing a lot of repetitive code in the first place
save us from explicitly dealing with extra tricky cases for exception safety, if things that should be automatically undone happen before or during a constructor
So if smart pointers are appropriately used, they take over the responsibility for undoing things, and a struct at the higher level doesn't need a destructor at all.
Then the one remaining reason to write a destructor which is more than just = default; or {} is then when implementing a brand new RAII handle, if it's not something just easily done by std::unique_ptr. And since an RAII handle should hold just one action to undo, that's usually a one-statement destructor.

unique_ptr in class how to work with them

I am implementing AVL tree in C++ and using unique_ptr for children.
struct Node
{
const int key;
std::unique_ptr<Node> left, right;
Node* parent;
std::size_t height; ///< for avl tree.
Node(const int key) : key(key), height(0) {}
};
class AVL
{
std::unique_ptr<Node> root;
public:
AVL(int rootKey) : root(std::unique_ptr<Node>(new Node(rootKey))) {
}
void insert(std::unique_ptr<Node> newNode) {
std::unique_ptr<Node> & node = root;
Node* parentWeak;
while(node.get()) {
parentWeak = node->parent;
if (node->key == newNode->key)
throw std::runtime_error("Key already present");
if (node->key < newNode->key)
node = node->right;
else
node = node->left;
}
auto parent = parentWeak;
const int key = newNode->key;
if (parent == nullptr) {
// there is no root
root = std::move(newNode);
} else {
if (parent->key < newNode->key) {
assert(NULL == parent->right.get());
parent->right = std::move(newNode);
} else {
assert(NULL == parent->left.get());
parent->left = std::move(newNode);
}
}
// Now increment the height upto down.
incrementHeight(key);
// balance starting from parent upwards untill we find some dislanace in height
balance(parent, key);
}
};
I am getting compiler errors on line node = node->right;. Which is right because it can be possible with only std::move semantics. but that would be wrong because i want to just iterate over the tree, otherwise it would just remove them from the child-list.
However, i need the unique_ptr also, as it would passed in function balance as it would modify the pointers and re-balance the tree.
If i use shared_ptr it would all work. However, i do not need to share the ownership with others. Or am i misunderstanding ownership ?
Your problem seems to be caused by a lack of understanding how to use unique_ptr in real programs, which is related to the concept of ownership. If a something owns an object, it means, this something is responsible for keeping the object alive as long as this something keeps owning the object, and is responsible to destroy the object as soon as nothing owns the object anymore.
Both unique_ptr and shared_ptr can be used to own objects. The difference, you seem to be aware of, is that an object pointed to by unique_ptr can only have a single owner, while there might be multiple shared_ptr objects sharing ownership of a specific object. If a unique_ptr is destroyed or assigned a different value, by definition it can destroy the object it previously pointed to, as a unique_ptr is the single (unique) owner of an object.
Now you have to think about your tree: You can use shared_ptr for everything, which will likely (seems to) work, as objects are kept alive as long as there are references to them. If there really is the parent member in node which you use in your method but did not declare in the node structuer, you would be likely to create reference cycles, though, creating the danger of keeping objects around way too long (or even forever, this is called a memory leak), as shared_ptr in C++ is purely reference-counted. Two objects containing shared_ptrs pointing to each other keep themselves alive forever, even if no other pointer points to them. It seems like in your shared_ptr solution, the parent member was a weak_ptr which is a sensible way to work around this problem, although possibly not the most efficient one.
You seem to want to improve performance and strictness of your code by using unique_ptr instead of shared_ptr which is commonly accepted as a very good idea, as it forces you to deal with ownership in much greater detail. Your choice that the tree owns the root node, and each node owns the children is a sound design. You seem to have removed the parent pointer, because it can not be a unique_ptr, as in that case, a node would be owned by its parents and any childrens it might have, violating the constraint that an object pointed to by unique_ptr may only have one owner. Also, the parent member can not be a weak_ptr, as weak_ptr can only be used with objects managed by shared_ptr. If you want to translate a design from shared_ptr to unique_ptr, you should consider changing weak_ptrs into raw pointers. A non-owning pointer to an object managed by unique_ptr that detects expiration of that object does not exist (it would not be effienctly implementable with the typical C++ memory management). If you need the property of being able to detect a non-owning pointer to be stale, keep using shared_ptr. The overhead for tracking non-owning pointers is almost as big as full shared-ownership semantics, so there is no middle ground in the standard library.
Finally, let's discuss the insert method. The node variable quite surely is not what you want. You correctly found out (possibly by a compiler error message) that node can not be a unique_ptr, as that would take away ownership from the tree object. In fact, having this variable refer to the root pointer in the tree is the right solution, as you don't want to mess around with ownership at this point, but just want to be able to get a grip on some node. But declaring it as a reference does not fit to the way you want to use it, because in C++ you can't re-seat a reference. What you do is you declare node to be just another name for this->root, so if you assign to node, you are overwriting your root node pointer. I am sure this is not what you intended. Instead, you want node to refer to a different object than it referred to before, so it needs to be something that references the root node and can be made to refer to something else. In C++, this means you want a pointer (as Jarod42 said in the comment). You have two choices at hand for the loop scanning the position where to insert:
Use a raw pointer to node instead of a unique_ptr to node. As you don't need ownership, a raw pointer to node is good enough: You can be sure the owning pointer (this->root) keeps alive as long a you need it, so there is no danger of the object disappearing.
Use a raw pointer to unique_ptr to node. This is essentially your approach, fixed to use a pointer instead of a reference.
As you say, you later need the unique_ptr to pass it to the balance function. If the balance function works out as it is now, and needs a unique_ptr argument, the decision is made: Having a copy of the raw pointer in node just doesn't do what you want, so you need the pointer-to-unique_ptr.

Pointers vs auto_ptr vs shared_ptr

I was recently introduced to the existence of auto_ptr and shared_ptr and I have a pretty simple/naive question.
I try to implement a data structure and I need to point to the children of a Node which (are more than 1 and its) number may change. Which is the best alternative and why:
class Node
{
public:
// ...
Node *children;
private:
//...
}
class Node
{
public:
// ...
share_ptr<Node> children;
private:
//...
}
I am not sure, but I think auto_ptr does not work for arrays. I am not, also, sure about whether I should use double pointers. Thanks for any help.
You're right that auto_ptr doesn't work for arrays. When it destroys the object it owns, it uses delete object;, so if you used new objects[whatever];, you'll get undefined behavior. Perhaps a bit more subtly, auto_ptr doesn't fit the requirements of "Copyable" (as the standard defines the term) so you can't create a container (vector, deque, list, etc.) of auto_ptr either.
A shared_ptr is for a single object as well. It's for a situation where you have shared ownership and need to delete the object only when all the owners go out of scope. Unless there's something going on that you haven't told us about, chances are pretty good that it doesn't fit your requirements very well either.
You might want to look at yet another class that may be new to you: Boost ptr_vector. At least based on what you've said, it seems to fit your requirements better than either auto_ptr or shared_ptr would.
I have used std::vector<std::shared_ptr<Node> > children successfully in a similar situation.
The main benefit of using a vector of shared_ptrs rather than an array is that all of the resource management is handled for you. This is especially handy in two situations:
1) When the vector is no longer in scope, it automatically calls delete on all of its contents. In this case, the reference count of the child Node will drop by 1 and if nothing else is referencing it, delete will be called on the object.
2) If you are referencing the Node elsewhere, there is no risk of being left with a dangling pointer to a deleted object. The object will only be deleted when there are no more references to it.
Unless you want behaviour that is substantially more complicated (perhaps there is a reason why an array is necessary), I would suggest this might be a good approach for you.
A simple implementation of the idea:
class Node {
private:
T contents;
std::vector<std::shared_ptr<Node> > children;
public:
Node(T value) : contents(value) {};
void add_child(T value) {
auto p = std::make_shared<Node>(value);
children.push_back(p);
}
std::shared_ptr<Node> get_child(size_t index) {
// Returning a shared pointer ensures the node isn't deleted
// while it is still in use.
return children.at(index);
}
void remove_child(size_t index) {
// The whole branch will be destroyed automatically.
// If part of the tree is still needed (eg. for undo), the
// shared pointer will ensure it is not destroyed.
children.erase(children.begin() + index);
}
};
auto_ptr is deprecated in favor of std::unique_ptr and btw. std::unique_ptr does work for arrays. You just need c++11 support. And there is already lots of resources about smart pointers and move semantics out there. The main difference between auto_ptr and unique_ptr is that auto_ptr does a move when you call the copy constructor and unique_ptr forbids the copy constructor, but allows a move when calling the move constructor. Therefore you need c++11 support with move semantics.
Stroustrup discusses the question of "What is an auto_ptr and why isn't there an auto_array" and concludes that there no need for the latter since the desired functionality can be accomplished with a vector.
http://www.stroustrup.com/bs_faq2.html#auto_ptr

What are potential dangers when using boost::shared_ptr?

What are some ways you can shoot yourself in the foot when using boost::shared_ptr? In other words, what pitfalls do I have to avoid when I use boost::shared_ptr?
Cyclic references: a shared_ptr<> to something that has a shared_ptr<> to the original object. You can use weak_ptr<> to break this cycle, of course.
I add the following as an example of what I am talking about in the comments.
class node : public enable_shared_from_this<node> {
public :
void set_parent(shared_ptr<node> parent) { parent_ = parent; }
void add_child(shared_ptr<node> child) {
children_.push_back(child);
child->set_parent(shared_from_this());
}
void frob() {
do_frob();
if (parent_) parent_->frob();
}
private :
void do_frob();
shared_ptr<node> parent_;
vector< shared_ptr<node> > children_;
};
In this example, you have a tree of nodes, each of which holds a pointer to its parent. The frob() member function, for whatever reason, ripples upwards through the tree. (This is not entirely outlandish; some GUI frameworks work this way).
The problem is that, if you lose reference to the topmost node, then the topmost node still holds strong references to its children, and all its children also hold a strong reference to their parents. This means that there are circular references keeping all the instances from cleaning themselves up, while there is no way of actually reaching the tree from the code, this memory leaks.
class node : public enable_shared_from_this<node> {
public :
void set_parent(shared_ptr<node> parent) { parent_ = parent; }
void add_child(shared_ptr<node> child) {
children_.push_back(child);
child->set_parent(shared_from_this());
}
void frob() {
do_frob();
shared_ptr<node> parent = parent_.lock(); // Note: parent_.lock()
if (parent) parent->frob();
}
private :
void do_frob();
weak_ptr<node> parent_; // Note: now a weak_ptr<>
vector< shared_ptr<node> > children_;
};
Here, the parent node has been replaced by a weak pointer. It no longer has a say in the lifetime of the node to which it refers. Thus, if the topmost node goes out of scope as in the previous example, then while it holds strong references to its children, its children don't hold strong references to their parents. Thus there are no strong references to the object, and it cleans itself up. In turn, this causes the children to lose their one strong reference, which causes them to clean up, and so on. In short, this wont leak. And just by strategically replacing a shared_ptr<> with a weak_ptr<>.
Note: The above applies equally to std::shared_ptr<> and std::weak_ptr<> as it does to boost::shared_ptr<> and boost::weak_ptr<>.
Creating multiple unrelated shared_ptr's to the same object:
#include <stdio.h>
#include "boost/shared_ptr.hpp"
class foo
{
public:
foo() { printf( "foo()\n"); }
~foo() { printf( "~foo()\n"); }
};
typedef boost::shared_ptr<foo> pFoo_t;
void doSomething( pFoo_t p)
{
printf( "doing something...\n");
}
void doSomethingElse( pFoo_t p)
{
printf( "doing something else...\n");
}
int main() {
foo* pFoo = new foo;
doSomething( pFoo_t( pFoo));
doSomethingElse( pFoo_t( pFoo));
return 0;
}
Constructing an anonymous temporary shared pointer, for instance inside the arguments to a function call:
f(shared_ptr<Foo>(new Foo()), g());
This is because it is permissible for the new Foo() to be executed, then g() called, and g() to throw an exception, without the shared_ptr ever being set up, so the shared_ptr does not have a chance to clean up the Foo object.
Be careful making two pointers to the same object.
boost::shared_ptr<Base> b( new Derived() );
{
boost::shared_ptr<Derived> d( b.get() );
} // d goes out of scope here, deletes pointer
b->doSomething(); // crashes
instead use this
boost::shared_ptr<Base> b( new Derived() );
{
boost::shared_ptr<Derived> d =
boost::dynamic_pointer_cast<Derived,Base>( b );
} // d goes out of scope here, refcount--
b->doSomething(); // no crash
Also, any classes holding shared_ptrs should define copy constructors and assignment operators.
Don't try to use shared_from_this() in the constructor--it won't work. Instead create a static method to create the class and have it return a shared_ptr.
I've passed references to shared_ptrs without trouble. Just make sure it's copied before it's saved (i.e., no references as class members).
Here are two things to avoid:
Calling the get() function to get the raw pointer and use it after the pointed-to object goes out of scope.
Passing a reference of or a raw pointer to a shared_ptr should be dangerous too, since it won't increment the internal count which helps keep the object alive.
We debug several weeks strange behavior.
The reason was:
we passed 'this' to some thread workers instead of 'shared_from_this'.
Not precisely a footgun, but certainly a source of frustration until you wrap your head around how to do it the C++0x way: most of the predicates you know and love from <functional> don't play nicely with shared_ptr. Happily, std::tr1::mem_fn works with objects, pointers and shared_ptrs, replacing std::mem_fun, but if you want to use std::negate, std::not1, std::plus or any of those old friends with shared_ptr, be prepared to get cozy with std::tr1::bind and probably argument placeholders as well. In practice this is actually a lot more generic, since now you basically end up using bind for every function object adaptor, but it does take some getting used to if you're already familiar with the STL's convenience functions.
This DDJ article touches on the subject, with lots of example code. I also blogged about it a few years ago when I first had to figure out how to do it.
Using shared_ptr for really small objects (like char short) could be an overhead if you have a lot of small objects on heap but they are not really "shared". boost::shared_ptr allocates 16 bytes for every new reference count it creates on g++ 4.4.3 and VS2008 with Boost 1.42. std::tr1::shared_ptr allocates 20 bytes. Now if you have a million distinct shared_ptr<char> that means 20 million bytes of your memory are gone in holding just count=1. Not to mention the indirection costs and memory fragmentation. Try with the following on your favorite platform.
void * operator new (size_t size) {
std::cout << "size = " << size << std::endl;
void *ptr = malloc(size);
if(!ptr) throw std::bad_alloc();
return ptr;
}
void operator delete (void *p) {
free(p);
}
Giving out a shared_ptr< T > to this inside a class definition is also dangerous.
Use enabled_shared_from_this instead.
See the following post here
You need to be careful when you use shared_ptr in multithread code. It's then relatively easy to become into a case when couple of shared_ptrs, pointing to the same memory, is used by different threads.
The popular widespread use of shared_ptr will almost inevitably cause unwanted and unseen memory occupation.
Cyclic references are a well known cause and some of them can be indirect and difficult to spot especially in complex code that is worked on by more than one programmer; a programmer may decide than one object needs a reference to another as a quick fix and doesn't have time to examine all the code to see if he is closing a cycle. This hazard is hugely underestimated.
Less well understood is the problem of unreleased references. If an object is shared out to many shared_ptrs then it will not be destroyed until every one of them is zeroed or goes out of scope. It is very easy to overlook one of these references and end up with objects lurking unseen in memory that you thought you had finished with.
Although strictly speaking these are not memory leaks (it will all be released before the program exits) they are just as harmful and harder to detect.
These problems are the consequences of expedient false declarations: 1. Declaring what you really want to be single ownership as shared_ptr. scoped_ptr would be correct but then any other reference to that object will have to be a raw pointer, which could be left dangling. 2. Declaring what you really want to be a passive observing reference as shared_ptr. weak_ptr would be correct but then you have the hassle of converting it to share_ptr every time you want to use it.
I suspect that your project is a fine example of the kind of trouble that this practice can get you into.
If you have a memory intensive application you really need single ownership so that your design can explicitly control object lifetimes.
With single ownership opObject=NULL; will definitely delete the object and it will do it now.
With shared ownership spObject=NULL; ........who knows?......
If you have a registry of the shared objects (a list of all active instances, for example), the objects will never be freed. Solution: as in the case of circular dependency structures (see Kaz Dragon's answer), use weak_ptr as appropriate.
Smart pointers are not for everything, and raw pointers cannot be eliminated
Probably the worst danger is that since shared_ptr is a useful tool, people will start to put it every where. Since plain pointers can be misused, the same people will hunt raw pointers and try to replace them with strings, containers or smart pointers even when it makes no sense. Legitimate uses of raw pointers will become suspect. There will be a pointer police.
This is not only probably the worst danger, it may be the only serious danger. All the worst abuses of shared_ptr will be the direct consequence of the idea that smart pointers are superior to raw pointer (whatever that means), and that putting smart pointers everywhere will make C++ programming "safer".
Of course the mere fact that a smart pointer needs to be converted to a raw pointer to be used refutes this claim of the smart pointer cult, but the fact that the raw pointer access is "implicit" in operator*, operator-> (or explicit in get()), but not implicit in an implicit conversion, is enough to give the impression that this is not really a conversion, and that the raw pointer produced by this non-conversion is an harmless temporary.
C++ cannot be made a "safe language", and no useful subset of C++ is "safe"
Of course the pursuit of a safe subset ("safe" in the strict sense of "memory safe", as LISP, Haskell, Java...) of C++ is doomed to be endless and unsatisfying, as the safe subset of C++ is tiny and almost useless, as unsafe primitives are the rule rather than the exception. Strict memory safety in C++ would mean no pointers and only references with automatic storage class. But in a language where the programmer is trusted by definition, some people will insist on using some (in principle) idiot-proof "smart pointer", even where there is no other advantage over raw pointers that one specific way to screw the program state is avoided.