0

When I run this code:

#include <iostream> #include <memory> struct Adaptee { int value = 0; }; class Adapter { private: Adaptee* adaptee; public: Adapter (Adaptee* a) : adaptee(a) {} void increaseValueByOne() {adaptee->value++;} }; int main() { Adaptee* a = new Adaptee; Adapter* adapter = new Adapter(a); adapter->increaseValueByOne(); delete adapter; std::cout << "a->value = " << a->value << std::endl; } 

I get the output: a->value = 1. But when I run this:

struct Adaptee { int value = 0; }; class Adapter { private: std::shared_ptr<Adaptee> adaptee; public: Adapter (Adaptee* a) : adaptee (std::shared_ptr<Adaptee>(a)) {} void increaseValueByOne() {adaptee->value++;} }; int main() { Adaptee* a = new Adaptee; Adapter* adapter = new Adapter(a); adapter->increaseValueByOne(); delete adapter; std::cout << "a->value = " << a->value << std::endl; } 

I get: a->value = 7738248

Why is this? Isn't the use_count of adapter->adaptee simply changing from 2 to 1, so nothing should go wrong? When I remove the line 'delete adapter;' everything is fine though, but then there is leaking.

5
  • 1
    Why don't you trace it line-by-line in the debugger? It's a great way to figure out why any piece of code runs the way it does. Commented Aug 22, 2014 at 16:05
  • you're kinda misusing allocation and shared pointers here. If your main had Adapter adapter{new Adaptee}; you wouldn't be able to get this odd behavior. Commented Aug 22, 2014 at 16:14
  • by using a shared pointer you are giving Adapter at least partial ownership of the Adaptee object. When Adapter dies, it deallocated the object it points to. When you mix shared pointers with delete you're on your own, that's not how the library is suppose to be used. Commented Aug 22, 2014 at 16:15
  • Why would the use count be 2? There's only one shared pointer, which dies with delete adapter. Commented Aug 22, 2014 at 16:44
  • @ Mike. Yes, I realize that now. I thought a owned it too when it wasn't even a shared_ptr. Had I realized this before, this post never would have shown up. Commented Aug 22, 2014 at 16:56

4 Answers 4

4

Because in the second case Adaptee instance is deleted by the destructor of shared_ptr when you delete adapter as it's the last shared_ptr owning that object. See the doc.

If you want to keep the adaptee alive after you delete adapter, you should wrap it into shared_ptr from the start:

class Adapter { private: std::shared_ptr<Adaptee> adaptee; public: Adapter (const std::shared_ptr<Adaptee>& a) : adaptee (a) {} void increaseValueByOne() {adaptee->value++;} }; int main() { std::shared_ptr<Adaptee> a(new Adaptee); Adapter* adapter = new Adapter(a); adapter->increaseValueByOne(); delete adapter; std::cout << "a->value = " << a->value << std::endl; } 
Sign up to request clarification or add additional context in comments.

4 Comments

I thought it is shared by a and adapter->adaptee, so the use_count was 2. But let's say you are right, then the only way to delete adapter in this case, is to declare it as a smart pointer?
If you just want to output the correct a->value you can simply move the printing code before delete adapter.
@ Anton. I want to keep a and its intended value of 1 after deleting adapter.
Then wrap adaptee in shared_ptr from the start. Check the updated answer.
2

In the first case, the line

delete adapter; 

did nothing to adaptee. It is still a valid pointer.

In the second case, the same line calls the destructor of the shared_ptr, which deallocates the memory pointed to by adaptee. Hence, adaptee is not a valid pointer and you can expect undefined behavior if you dereference it.

Comments

1

The principle of the shared_ptr is to manage the deletion of the pointed object when it's no longer used.

When you create the shared_ptrin your adapter, it takes ownership of the object and sets the use count to 1. When the adapter is deleted, so is the shared pointer. THe use count (that you can display with use_count()) is hence automatically decremented and reaches zero. So the object is deleted and a points to an invalid address.

If you dont' want the shared_ptr to release the memory of a when deleting adapter, you should create your original pointer as shared_pointer as well:

int main() { auto a = std::make_shared<Adaptee>(); //shared pointer instead of Adaptee* a = new Adaptee; ... } 

And add an additional constructor in class Adapter:

Adapter(std::shared_ptr<Adaptee> a) : adaptee(a) {} 

With this consistent use of shared_ptr,the use count of shared pointer would go to 1 when adapter is deleted, and the rest of your code will work as you exepect, printing 1 as in your original version.

Comments

1

To expand on what others are saying, when you give a raw heap allocated pointer to a shared_ptr, you are giving the shared_ptr control over the lifetime of the resource. shared_ptr keeps a reference count of how many shared_ptrs are pointing to a resource. When that count is 0 the resource is cleaned up.

adaptee (std::shared_ptr<Adaptee>(a)) 

Here, the reference count is 1 because there is 1 shared_ptr pointing to the resource a.

delete adapter; 

causes the shared_ptr in adapter to go out of scope. The shared_ptr's destructor is called, which decrements the reference count. Since the reference count is now 0, delete is called on a.

std::cout << "a->value = " << a->value << std::endl; 

Here, a is no longer a valid pointer because delete was called in the destructor of the shared_ptr.

If you want a to stay valid you should do the following:

struct Adaptee { int value = 0; }; class Adapter { private: std::shared_ptr<Adaptee> adaptee; public: Adapter (std::shared_ptr<Adaptee> a) : adaptee(a) {} void increaseValueByOne() {adaptee->value++;} }; int main() { std::shared_prt<Adaptee> a = std::make_shared<Adaptee>(); //reference count is 1 Adapter* adapter = new Adapter(a); //reference count is 2 adapter->increaseValueByOne(); delete adapter; //reference count is 1 std::cout << "a->value = " << a->value << std::endl; } // reference count is 0; memory is deallocated 

2 Comments

Ok. Everything is perfectly clear to me now. I realize my fallacy: I thought in the original code, the use_count of adapter->adaptee was 2 (owned by itself and a), but a was not a shared_ptr so the use count was actually 1 (and then turned to 0 when adapter was deleted). I suppose to be consistent, adapter should be a shared_ptr as well (and don't bother calling delete on it at all).
Consider using unique_ptr instead of shared_ptr for adapter. Or just stack allocate it by doing Adapter adapter(a); adapter.increaseValueByOne();

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.