Reputation: 497
Thanks for helping in advance. I am reading the book More Effective C++ by Scott Meyers, but one simple program in Item 29 "Reference Counting" really confuses me. The program is copied here:
String::String(const String& rhs): value(rhs.value)
{
++value->refCount;
}
Then code:
String s1("More Effective C++");
String s2=s1;
I am really confused why s1 and s2 will both have a refCount 2. What I understand is that since the copy constructor is pass-by-reference-to-const, after s2=s1,s2.refCount will become 2, while s1.refCount will not change at all. Please correct me!! Thanks again.
Best Regards.
Upvotes: 4
Views: 656
Reputation: 84151
value
in this case is a pointer, and const
-ness does not propagate to object being pointed to, so the refCount
is mutable here.
The point of reference counting is to share same object representation without re-creating it until all references disappear, i.e. reference count drops to zero. At this point the representation is de-allocated.
This works great for read-only objects, so if one of the referring instances wants to change that shared representation, it's usually cloned and ref-counting starts from one again.
Then there are issues with making reference count thread-safe. Sutter wrote extensively about this, see gotw #43, gotw #44, and gotw #45.
Upvotes: 3
Reputation: 234364
If the reference count used by s1
was 1, then it would take down the string with it when it died. Consider the following:
String s2;
{
String s1("More Effective C++");
s2 = s1;
} // A
At the point A, s1
dies. If its refcount is 1, it will clean up the storage it was sharing with s2
, and s2
would be using invalid storage.
The reference count is not associated to each object. As you can see from the example I gave, that would be worthless because the reference count would never be trustworthy as an indicator that it is safe to clean up.
The reference count is associated with the pieces of storage those objects share. There is only one reference count for both s1
and s2
. The two share a piece of storage with "More Effective C++" in it. That means there are two references to that piece of storage. Each of the two needs to know that there are two, so that they do not clean up storage that the other is using.
Upvotes: 1
Reputation: 29047
value
is a pointer to an underlying implementation struct. The string copy constructor copies the pointer into the new object (s2
) and increments the reference count of the pointed to implementation struct. However, remember that the original object (s1
) has the same pointer, so the reference count as seen from s1 will have been incremented as well. There is only one underlying implementation struct, so acting on it from one String object affects all other String objects that share that implementation struct. That is the whole point of reference counting!
Upvotes: 0
Reputation: 476940
The reference count has to reside in a separate, shared memory:
struct Foo
{
unsigned int * refcount; // shared among all "equal" objects!
Foo() : refcount(new auto {1U}) { }
Foo(Foo const & rhs) : refcount(rhs.refcount) { ++*refcount; }
~Foo() { --*refcount; if (*refcount == 0) { delete refcount; } }
Foo & operator=(Foo const & rhs)
{
if (this == std::addressof(rhs)) { return *this; }
--*refcount;
if (*refcount == 0) { delete refcount; }
refcount = rhs.refcount;
++*refcount;
return *this;
}
// etc.
};
Upvotes: 0
Reputation: 168596
I understand that
s2.refCount
will become 2, whiles1.refCount
will not change at all.
There is your misunderstanding. There is no such animal as s2.refCount
nor s1.refCount
. Rather, the variables are called s2.value->refCount
and s1.value->refCount
. Notice that s2.value
== s1.value
, so they inherently share the same refCount
member.
Upvotes: 3