weak_ptrT and make_shared()

Started by
6 comments, last by Shinkage 13 years, 9 months ago
Hi,

I have a little question: as I understand make_shared() creates a shared_ptr<> and allocates one memory block for both the created object and the reference counter.

Since we can have a weak_ptr<> created from that shared_ptr<>, when all strong refereences die, the pointed object is destructed, but the memory it occupied is not freed until ALL weak_ptr<>s also die.

Am I understanding this correctly?

Zbyl
Advertisement
The lifetime of the object is controlled by the shared_ptr's (strong). The weak pointers will be invalidated upon the destruction of the object.

[edit]
Boost - weak_ptr - expired() This function allows you to test if the object still exists or not.

"I can't believe I'm defending logic to a turing machine." - Kent Woolworth [Other Space]

Quote:The lifetime of the object is controlled by the shared_ptr's (strong).

Yep. Destructor gets called allright. But I don't think that the memory is freed.

Quote:The weak pointers will be invalidated upon the destruction of the object.

But the info: "You are invalidated" is kept in the reference counting block of a shared_ptr<>, and if the object was in the same memory block, it can't be freed (as long as weak_ptr<>s live).

At least that's my understanding of it. I just want to be sure.
Quote:Original post by Zbychs
I have a little question: as I understand make_shared() creates a shared_ptr<> and allocates one memory block for both the created object and the reference counter.
Zbyl


Not quite. It actually allocates TWO memory blocks: the class itself (member name 'px'), and a separate memory block for tracking the reference count (member name 'pn'). The lifetime of the class (the 'px' member) is controlled only by shared_ptr references--when the last shared_ptr goes out of scope, the pointee is destroyed. The liftime of the reference count, on the other hand, is controlled by both shared_ptr AND weak_ptr references.

If you look inside the 'pn' member, you'll see it actually has two different reference counts: 'use_count_' and 'weak_count_'. The pointee gets destroyed when use_count hits 0, and the reference counter gets destroyed when both use_count and weak_count hit 0.

EDIT: These are private member names for the boost implementation. Other implementations may differ in the details.
Hmmm? boost 1.43.0 with MSVC 2008:
#include <crtdbg.h>#include <boost/shared_ptr.hpp>#include <boost/weak_ptr.hpp>#include <boost/make_shared.hpp>#include <windows.h>struct Foo {  Foo() { OutputDebugStringA("Foo::Foo()\n"); }  Foo(const Foo &) { OutputDebugStringA("Foo::Foo(const Foo &)\n"); }  ~Foo() { OutputDebugStringA("Foo::~Foo()\n"); }};int main(int, char **) {  _CrtMemState ms1, ms2, diff;  _CrtMemCheckpoint(&ms1);  boost::shared_ptr<Foo> s_ptr = boost::make_shared<Foo>();  OutputDebugStringA("make_shared() called\n");  _CrtMemCheckpoint(&ms2);  _CrtMemDifference(&diff, &ms1, &ms2);  _CrtMemDumpStatistics(&diff);  boost::weak_ptr<Foo> w_ptr = s_ptr;  s_ptr.reset();    OutputDebugStringA("\nshared_ptr gone, weak_ptr present\n");  _CrtMemCheckpoint(&ms2);  _CrtMemDifference(&diff, &ms1, &ms2);  _CrtMemDumpStatistics(&diff);    w_ptr.reset();  OutputDebugStringA("\nshared_ptr and weak_ptr gone\n");  _CrtMemCheckpoint(&ms2);  _CrtMemDifference(&diff, &ms1, &ms2);  _CrtMemDumpStatistics(&diff);}

Debug output:

Foo::Foo()
make_shared() called
0 bytes in 0 Free Blocks.
20 bytes in 1 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 0 bytes.
Total allocations: 20 bytes.
Foo::~Foo()

shared_ptr gone, weak_ptr present
0 bytes in 0 Free Blocks.
20 bytes in 1 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 0 bytes.
Total allocations: 20 bytes.

shared_ptr and weak_ptr gone
0 bytes in 0 Free Blocks.
0 bytes in 0 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 0 bytes.
Total allocations: 20 bytes.
I got the same results as you, with Visual Studio 2010.
I've changed your code a bit and the results are the following:

#include <crtdbg.h>#include <boost/shared_ptr.hpp>#include <boost/weak_ptr.hpp>#include <boost/make_shared.hpp>#include <windows.h>struct Foo {  Foo() { OutputDebugStringA("Foo::Foo()\n"); }  Foo(const Foo &) { OutputDebugStringA("Foo::Foo(const Foo &)\n"); }  ~Foo() { OutputDebugStringA("Foo::~Foo()\n"); }  double data;};int main(int, char **) {  _CrtMemState ms1, ms2, diff;  _CrtMemCheckpoint(&ms1);  //boost::shared_ptr<Foo> s_ptr = boost::make_shared<Foo>();  boost::shared_ptr<Foo> s_ptr = boost::shared_ptr<Foo>(new Foo());  OutputDebugStringA("make_shared() called\n");  _CrtMemCheckpoint(&ms2);  _CrtMemDifference(&diff, &ms1, &ms2);  _CrtMemDumpStatistics(&diff);  boost::weak_ptr<Foo> w_ptr = s_ptr;  s_ptr.reset();    OutputDebugStringA("\nshared_ptr gone, weak_ptr present\n");  _CrtMemCheckpoint(&ms2);  _CrtMemDifference(&diff, &ms1, &ms2);  _CrtMemDumpStatistics(&diff);    w_ptr.reset();  OutputDebugStringA("\nshared_ptr and weak_ptr gone\n");  _CrtMemCheckpoint(&ms2);  _CrtMemDifference(&diff, &ms1, &ms2);  _CrtMemDumpStatistics(&diff);}


Debug Output:

Foo::Foo()
make_shared() called
0 bytes in 0 Free Blocks.
24 bytes in 2 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 0 bytes.
Total allocations: 24 bytes.
Foo::~Foo()

shared_ptr gone, weak_ptr present
0 bytes in 0 Free Blocks.
16 bytes in 1 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 0 bytes.
Total allocations: 24 bytes.

shared_ptr and weak_ptr gone
0 bytes in 0 Free Blocks.
0 bytes in 0 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 0 bytes.
Total allocations: 24 bytes.

I've added a member to the foo structure, to be able to tell which memory gets freed. It seems that the data of the foo object is freed right when the shared_ptr is reset', and the 16 bytes left are the ones for the px member of boost::shared_ptr.

So boost::make_shared, as SiCrane demonstrated, allocates both the object and the reference count in one block, while my version uses two blocks, obviously...

Using boost 1.43 and Visual Studio 2010.
Thanks guys. That clarifies everything.
Ah, yeah, I misread that one pretty badly. I was thinking object lifetime, but now that I reread it he wanted to know when memory was allocated/freed. Sorry, my mistake! The pointed-to object is constructed using a placement new inside the memory allocated for the reference counter (it just has a char[sizeof(T)] where it puts the allocated object).

This topic is closed to new replies.

Advertisement