I'm new to smart pointers and I'm trying to wrap around my head why a weak_ptr would expire after a dereference operator. The code I used to test is here:
#include <memory>
#include <iostream>
#include <vector>
using namespace std;
struct node
{
weak_ptr<node> parent;
shared_ptr<node> child;
int val;
};
shared_ptr<node> foo()
{
shared_ptr<node> a = make_shared<node>();
shared_ptr<node> b = make_shared<node>();
a->val = 30;
b->val = 20;
b->parent = a;
a->child = b;
return a;
}
int main()
{
shared_ptr<node> c = foo();
node d = *foo();
if (c->child->parent.expired())
{
cout << "weak ptr in c has expired." << endl;
}
if (d.child->parent.expired())
{
cout << "weak ptr in d has expired." << endl;
}
return 0;
}
The program outputs weak ptr in d has expired.
I don't understand why when d uses the dereference operator, it expires. In regards to this, is there anyway to prevent it (other than not dereferencing it)?
I tried as mrtnj suggested by changing the weak_ptr in node to shared_ptr but I think I have a memory leak. I changed the node class to
struct node
{
shared_ptr<node> parent;
shared_ptr<node> child;
int val;
};
and then modified the source code to add a tryCreate function.
void tryCreate()
{
node d = *foo();
}
and then called it in my main such that my main looks like
int main()
{
tryCreate();
return 0;
}
I used Visual Studio 2015's memory profiling and noticed that there were only allocations and no deallocations. I changed parent into a weak_ptr and I see deallocations. Am I doing something wrong or is it indeed a requirement to use weak_ptr in these cyclic condition?
A weak_ptr expires when the last shared_ptr that refers to the object, is destroyed.
In your code that happens in the statement
node d = *foo();
Here foo() returns a shared_ptr, which is the last shared_ptr that refers to that object (the parent object of the two created by foo). And this shared_ptr is a temporary that's destroyed right there, after the derefencing. That reduces the reference count to 0 and the weak_ptr expires.
Since the shared_ptr was the last one, the object is destroyed, which causes its child object to also be destroyed. Thus the later code that delves into these objects has undefined behavior.
Since d contains a shared_ptr to the child node, the child node is not destroyed at this point, as noted by Miles Budnek in a comment.
node d = *foo();
foo returns a shared_ptr which is keeping the parent node allocated in foo alive.
You then copy the contents of that into d but do not store the shared_ptr, so that is destroyed at the end of the statement. There are now no shared_ptr instances referencing the node instance dynamically allocated in foo, so the weak pointer references to it are now expired.
The dereference isn't the problem: the problem is the failure to capture the shared_ptr being returned.
Here:
node d = *foo();
you dereference shared_ptr, so d contains a copy of node which was created in foo in line:
shared_ptr<node> a = make_shared<node>();
this a will be destroyed just after node d = *foo();. This is because parent is only a weak_ptr inside node.
In regards to this, is there anyway to prevent it (other than not dereferencing it)?
Not dereferencing seems to be good aproach.
You could switch from weak_tr<node> parent; to shared_ptr<node> parent;. Other solution would be to keep a global shared_ptr<node> root; which would keep a reference to your a. But It depends on what your code really is going todo.
Related
I was trying to build a linked list without allocating memory dynamically. Note that I can build linked list using new operator.
The below code does not work. When I debugged it I found out (as much as I can understand) that Node a(n, head) is allocated at the same address in the memory every time so the a stores pointer to itself. If someone can explain to me, it would be of great help.
class Node {
public:
int val;
Node *next;
Node(int n, Node *ptr = NULL) {
val = n;
next = ptr;
}
};
class LinkList {
Node *head = NULL;
public:
void insertNode(int n) {
Node a(n, head);
head = &a;
}
void print() {
Node* ptr = head;
while (ptr != NULL) {
cout << ptr->val << endl;
ptr = ptr -> next;
}
}
};
int main() {
LinkList a;
a.insertNode(3);
a.insertNode(4);
a.print();
return 0;
}
No, the same memory is not allocated to all objects of a class... but the same memory is allocated for the local variables of every function. Once a function returns, all its local variables are destroyed, so its local variable memory is now unused, and the next function call will use it for its local variables. Hence every time you call insertNode it uses the same memory to hold a. And when you call print, it uses that same memory to hold ptr.
This is just what usually happens. Not all compilers do it the same way, so you can't rely on it. And if you had extra function calls between main and insertNode then insertNode's variables wouldn't get the same addresses as they do when main calls insertNode directly.
Also note that because you aren't allowed to use pointers to variables that were already destroyed, the optimizer is allowed to guess that when you use a pointer, it points to a variable that hasn't been destroyed, and sometimes this causes really weird behaviour. So you mustn't use pointers to destroyed variables, ever, even if you would be okay with getting the wrong data. The technical term is undefined behaviour.
by intermittence reference I mean the following
shared_ptr<int> a = new int(100);
int* i = a;
shared_ptr<int> b = i;
would the reference count of b be two?
also if it is aware, would still be valid in the following code?
//
shared_ptr<int> a = new int(100);
{// workspace
int* i = a;
shared_ptr<int> b = i;
}// end of workspace
If no to above question, how could I achieve it?
I would to make my memory allocation safe by using smart pointer. I have a data structure tree that would create new tree nodes (pointers to allocated memory) that is either inserted into tree or passed out.
If it is within the tree, no problem, I can control the life cycle. If it is passed out, then I have no control.
The version you wrote in your example will not compile. But to answer the spirit of what I think your question is here is an example
#include <memory>
int main() {
std::shared_ptr<int> a = std::make_shared<int>(100);
int* i = a.get();
std::shared_ptr<int> b(i);
}
This does not have a and b related as far as reference counting. Instead they both (mistakenly) take ownership of the same underlying pointer, pointed at by i and will result in a double free or corruption when a falls out of scope since they both try to delete it.
The correct way for both shared_ptr to reference the same underlying pointer and have correct reference counting behavior is simply
#include <memory>
int main() {
std::shared_ptr<int> a = std::make_shared<int>(100);
std::shared_ptr<int> b = a;
}
Im having difficulty describing this problem succinctly so be kind.
I have a Tree object that has an attribute root which is a pointer to a node object. When I initialize the Tree object the root is unknown so i assign it to a nullptr.
In a function after some computation I find the root node of a complete binary tree. I now want to hand this value over to my Tree.root pointer. However since this function is removed from the stack after execution and Tree.root pointer appears empty when I run it later.
class Tree{
public:
Node *root;
Tree(){
root = nullptr;
}
};
void worker(Tree *t){
// Perform some computation
// Since the var rootFound only exists in this function.
// After executing doesn't the memory address reallocated
// and therefore the root points to an unknown memory address?
t-> root = &rootFound;
}
int main(){
Tree t{};
Tree *ptr = &t;
worker(t);
// t pointer is null
return 0;
}
I was thinking I could assign the root pointer found by the function to the heap (use new ) and then assign my Tree pointer to it but Im not sure how to go about deleting this value. Also since any node has left and right Node pointer Im not sure if the pointers lose the memory address they are pointing too or if they too will be added to the heap.
I could also just be overthinking this.
Since you posted an incomplete code in your question, I will be forced to assume things.
I will assume your implementation look like this:
void worker(Tree *t){
Node rootFound;
// do stuff where eventually rootFound = something
t->root = &rootFound;
}
In this case, yes, t->root will point to a dead object once worker is finished.
I was thinking I could assign the root pointer found by the function to the heap (use new ) and then assign my Tree pointer to it but Im not sure how to go about deleting this value.
There is two kind of raw pointer in C++: owning pointer and non owning pointer.
If t->root is a owning pointer, it means you will call delete on it.
If t->root is not a owning pointer, it means you will not call delete on it.
Then if it is owning, you can totally do new Node{...} an assign to it.
If on the contrary it is not and you want to create an new tree in this function and delete it later, you will need to give an owning pointer back to the caller, something like this:
Node* worker(Tree* t) {
Node* rootFound = new Node{}; // create a whole new tree here
t->root = rootFound; // assign it to the tree
return rootFound; // return a owning pointer to the caller
}
Then, in your main:
int main(){
Tree t{};
Tree *ptr = &t;
Node* owning = worker(ptr);
// do stuff with t
// delete the owning pointer.
delete owning;
return 0;
}
Of course, there is a better way to separate owning and non owning pointer.
In modern C++, owning pointer are declared like this: std::unique_ptr<T> and non owning are written T* and assume you don't have to delete it.
If we change your data structure just a little bit to express what pointer is which, it would look something like this:
class Tree{
public:
// Here! Root is owning.
std::unique_ptr<Node> root;
Tree(){
root = nullptr;
}
};
// Tree is non-owning, so we write it just like before.
void worker(Tree* t){
// ...
}
The neat thing about std::unique_ptr is that is clears memory in its destructor so you don't have to worry about deleting:
int main() {
// make_unique will call new
std::unique_ptr<Node> node = std::make_unique<Node>();
// Here, unique_ptr will call delete
}
So in the end, Tree will clear up itself at the end of main:
int main(){
Tree t{};
Tree *ptr = &t;
// worker can do t->root = std::make_unique<Node>();
worker(ptr);
return 0;
// Here, t.root will call delete if not null
}
I'm trying to make a List class using the concept of linked lists, and while I was originally using the C++ standard new keyword, I decided to switch it out for the C++11 std::shared_ptr. However, I can't get the program to function properly when using smart pointers, as it crashes. Here are some bits of code before the change:
class List
{
public:
void push_back(...) {
Node *temp = new Node;
...
if (!head) {
head = temp;
return;
}
else {
Node *last = head;
...
last->next = temp;
}
}
...
private:
Node *head = nullptr;
};
And here's what it looks like with the change:
class List
{
public:
void push_back(...) {
std::shared_ptr<Node> temp(new Node);
...
if (!head) {
head = temp.get();
return;
}
else {
Node *last = head;
...
last->next = temp.get();
}
}
...
private:
Node *head = nullptr; // don't need this to be a smart ptr
};
I feel like the problem might be that head and last aren't dynamically allocated and maybe they need to be to work with a shared_ptr, but I'm not sure. What exactly am I doing wrong and how can I fix it? I really hope this isn't a duplicate because I can't seem to find anything that solves my problem. Thanks.
Edit:
Here's the Node struct:
struct Node{
int data;
Node* next;
};
The reason to have std::shared_ptr in the first place is to have std::shared_ptr take complete and full ownership of the pointer, and make it std::shared_ptr's responsibility to delete it, once the last reference to the pointer goes away. That's what std::shared_ptr is all about.
This means that once a pointer is placed into a std::shared_ptr, the std::shared_ptr now takes complete and full responsibility of managing the pointer. It owns it completely.
It, therefore, makes no sense to put a pointer into a std::shared_ptr ... and then immediately take it out:
head = temp.get();
There are reasons for the get() function to exist, but this isn't one of them.
In order to use std::shared_ptr correctly, everything must be a std::shared_ptr. head needs to be a std::shared_ptr:
std::shared_ptr<Node> head; // yes, it does need to be a smart ptr
Why does it need to be a std::shared_ptr? Well, if it's not, what do you think will happen when this:
std::shared_ptr<Node> temp(new Node);
Specifically, when this temp smart pointer gets destroyed, when this function returns? Well, since it will be the last std::shared_ptr that referenced this Node, it will happily delete it. The fact that you get() it earlier, and placed it into head doesn't matter. So now you have a head that points to a deleted Node. Hilarity ensues.
And this is why everything must be a std::shared_ptr. Not only head, but also Node's next member also needs to be a std::shared_ptr, too.
Now, there is a pitfall involving circular references, that comes into play when std::shared_ptr enters the picture. But that's going to be a different question.
Your main issue is that if you're going to use shared_ptr, best to use it all the way. Make next a shared_ptr instead of a raw one.
struct Node {
int data;
std::shared_ptr<Node> next;
}
What std::shared_ptr does under the hood is keep a count of how many references there are to a pointer. When you use copy constructors or operator= it increases a reference count. When an instance falls out of scope resulting in the destructor being invoked (or you give it a different pointer with operator=) the reference count decrements. When the count is zero the pointer is destroyed.
// pass by value invokes copy constructor (refcount + 1)
void myFunc(std::shared_ptr<MyClass> var) {
// Code using var
} // end of function invokes destructor (refcount - 1)
void run() {
std::shared_ptr<MyClass> ptr(new MyClass); // refcount = 1
myFunc(ptr); // refcount = 2
// After myFunc returns refcount = 1
}
int main() {
run(); // refcount = 1
// After run returns, refcount = 0 and the pointer is deleted
}
By using get() you introduce a pointer to memory that may be deleted at some point, regardless of whether or not that pointer is around. This can lead to segfaults as the raw pointers are pointing to memory that shared_ptr deleted.
This is because get() does not affect the reference count. How could it? It's not a shared_ptr any more so that class definition has no way of knowing what you do with it, or when it gets deleted. If get() increased the reference count there would be nothing to decrease it afterwards, and the memory would never be released. That's a memory leak!
In the following code, I am getting the following runtime exception (possibly memory leak) after return 1; and in destructor of Node().
Unhandled exception at 0x0f9bad4a (msvcp100d.dll) in test.exe: 0xC0000005: Access violation reading location 0xfeeefef2.
It's been a while since I used smart_ptr, so am trying to learn what am I doing wrong here ?
#include <vector>
#include <queue>
#include <memory>
#include <iostream>
using namespace std;
class Node;
typedef shared_ptr<Node> SharedNode;
class Node {
Node* parent;
vector< SharedNode > children;
int value;
//limiting construction
Node(int a_value):value(a_value),parent(0){}
Node(const Node ©); //non-construction-copyable
Node& operator=(const Node& copy); //non-copyable
public:
static SharedNode create(int a_value){
return SharedNode(new Node(a_value));
}
SharedNode addChild(SharedNode child){
child->parent = this;
children.push_back(child);
return child;
}
SharedNode getNode(int searchValue);
};
SharedNode Node::getNode(int searchValue){
// Breadth First Search
queue<SharedNode> que;
que.push(SharedNode(this));
while(!que.empty()){
SharedNode node = que.front();
que.pop();
if(node->value == searchValue)
return node;
vector<SharedNode>::iterator it;
for(it = node->children.begin(); it != node->children.end(); it++){
que.push(*it);
}
}
return 0;
}
int main(){
SharedNode node_ptr = Node::create(5);
for(int i = 0; i < 4; ++i)
node_ptr->addChild(Node::create(i));
cout << (node_ptr->getNode(-1) != 0 ? "Found" : "Not found");
return 1;
}
I think I'm messing up when I use shared_ptr on this, like: shared_ptr(this). But then, that's my guess.
What am I doing wrong here ?
The problem is from
que.push(SharedNode(this));
This creates a new shared pointer that now owns this. However, due to the create() method, there is another shared pointer that owns the same object. This can result in a double delete.
If you have a reason to use a shared pointer in this situation, the correct solution is enable_shared_from_this.
First, change the node definition to this.
class Node : public std::enable_shared_from_this<Node> { ...
Then change the offending line to
que.push(this->shared_from_this());
This causes it to return a shared_ptr that points to the object, but it is shared with the already existing shared_ptr, instead of being two separate shared_ptr objects.
Note, for the use of this->shared_from_this() to be legal, the object must be owned by a shared_ptr. You already have accomplished this via the static create() method, but I wanted to make sure you understood the limitation.
Edit: A brief explanation of shared_ptr ownership.
When you create a shared_ptr from a raw pointer using the constructor, it creates a reference object that contains both a pointer to the object and a reference count, which is used to determine how many shared_ptr objects point to it. A pointer to this reference object is then passed to all copies that are made from that original shared_ptr, with the reference count keeping track of how many shared_ptr objects refer to it.
When you call shared_ptr(this), there is no way for the shared pointer to know that this is owned by another shared pointer, and creates a new reference object. Once the one of them reaches a reference count of zero, the object will be deleted, despite the other shared_ptr reference object still pointing to it, resulting in a dangling pointer and the error you are seeing.
If you only need the children to exist when the parent exists, I would consider changing the Node to simply have a std::vector of other Nodes (remove the pointer). When the highest level node is destroyed via its destructor, it will destroy the vector, which destroys the children nodes, and so-on.
class Node
{
// whatever operations you need...
std::vector<Node> children;
}
Edit: As requested...
If you have a use case where you do really want to have the children outlive the parents, you'll have to deal with the parent pointer, since it could be destroyed before the children. One quick solution is determine if you really NEED the parent pointer, and eliminate it if you don't need it.
However, assuming you still want to retain it, you cannot use shared_ptr here. If you do that, you'll have a circular dependency, and neither will be destroyed automatically, which isn't what you want.
The solution here is to use std::weak_ptr. Basically, it interacts with the shared_ptr reference object in such a way that it doesn't prevent the destruction of the pointed to object.
class Node
{
private:
std::weak_ptr<Node> parent;
// Other constructors.
Node(int a_value):value(a_value),parent() {}
public:
SharedNode addChild(SharedNode child){
child->parent = this->shared_from_this(); // Initialize their pointer using
// your shared pointer
children.push_back(child);
return child;
}
// This function will return a shared_ptr to nullptr (and will evaluate false)
// if you have no parent, or if the parent node has been deleted
SharedNode getParent()
{
return parent.lock();
}
};
Consider what happens with the following code:
Node * dumb_ptr = new Node;
shared_ptr<Node> smart1 = dumb_ptr;
shared_ptr<Node> smart2 = dumb_ptr;
You now have two smart pointers both thinking they own the same object. One of them is going to delete the object, and the other one will try to use or delete that deleted object at some point. The way you fix this is by always creating a smart pointer from another smart pointer or from new. Best to never use any dumb pointers at all - that includes this.
shared_ptr<Node> smart1 = new Node;
shared_ptr<Node> smart2 = smart1;