question regarding the shared_ptr use_count

Discussion in 'C++' started by somenath, Feb 3, 2014.

  1. somenath

    somenath Guest

    I am not able to understand the behavior of the following program

    #include <iostream>
    #include<memory>
    #include<string>
    using namespace std;

    int main() {
    auto p1 = make_shared<string> (10,'S');
    cout<<"p1 use_count = "<<p1.use_count()<<endl;
    shared_ptr<string> p2(new string()) ;
    p1 = p2;
    cout<<"p2 use_count = "<<p2.use_count()<<endl;
    cout<<"second p1 use_count = "<<p1.use_count()<<endl;
    return 0;
    }
    Output
    ++++++++++++++++
    p1 use_count = 1
    p2 use_count = 2
    second p1 use_count = 2

    I can understand the first two print. At beginning p1 points to one string so the reference count is 1.
    When the statement p1=p2; executes p2's reference count gets incremented to 2 but at the same time I was expecting that p1's reference count to be decremented by 1 as p1 would be pointing to p2 now.
    Please let me know where my understanding is going wrong?
     
    somenath, Feb 3, 2014
    #1
    1. Advertisements

  2. somenath

    Ian Collins Guest

    p1 *is* p2. There are to references (p1 and p2) to the string initially
    assigned to p1. Both have to go out of scope for the string to be deleted.
     
    Ian Collins, Feb 3, 2014
    #2
    1. Advertisements

  3. somenath

    somenath Guest

    I couldn't follow you. Why p2 would refer to the "string initially
    assigned to p1"?
    p2 is defined as follows

    shared_ptr<string> p2(new string())
    So according to my understanding p2 would refer to the empty string.
    Then when the statement p1=p2; gets executed p1 also start referring to what ever p2 referring to. So the reference count of p2 will increase and also hoped that p1's reference count to be decremented by 1 but that does not happen.

    I think this way because the book I refer for understanding these concept says.
    "We can think of shared_ptr as if it has an associated counter,usually referred to as a reference count.
    ...
    ...
    p=q p and q are shared_ptrs holding pointers that can be converted to one another. Decrements p's reference count and increments q's count; deletes p's existing memory if p's count goes to 0"

    But I think it is better to think reference count as the count of pointers pointing to some memory location. In that case according to my program the following statement
    p1=p2;
    will increase the reference count of the memory location that p2 point as p1 also point to the same location.As p1 also point to the same meory location so when I print p1's reference count it prints the same count as p2.
    cout<<"second p1 use_count = "<<p1.use_count()<<endl;
    Please help me to get my understanding correct.
     
    somenath, Feb 3, 2014
    #3
  4. somenath

    Ian Collins Guest

    *Please clean up the awful mess google makes of your quotes!*
    I got p1 and p2 back to front, but the main point is still the same:
    they both refer to the same object, so they must have the same reference
    count.
    This happens to p in the assignment operator, before the value pf q is
    assigned to it.
    It does appear to be correct.
     
    Ian Collins, Feb 3, 2014
    #4
  5. The reference counter is no property of the shared_ptr. It is a property
    of the objects where the shared_ptr points to.
    shared_ptr instances do not have a reference counter.
    The reference counter of *p and *q are modified. p and q do not have
    reference counters.

    This is exactly what's going on.
    Internally shared_ptr uses helper objects to add the reference counter
    to arbitrary objects. So your code effectively does the following:

    auto p1 = make_shared<string> (10,'S');

    tmp1 = new string#1(10,'S')
    tmp2 = new helper_obj#1(1, tmp1)
    p1 = shared_ptr(tmp2)

    result:
    p1 -> helper_obj#1{ counter = 1, content -> string#1{"SSSSSSSSSS"} }

    cout<<"p1 use_count = "<<p1.use_count()<<endl;
    shared_ptr<string> p2(new string()) ;

    tmp1 = new string#2()
    tmp2 = new helper_obj#2(1, tmp2)
    p2 = shared_ptr(tmp2)

    result:
    p1 -> helper_obj#1{ counter = 1, content -> string#1{"SSSSSSSSSS"} }
    p2 -> helper_obj#2{ counter = 1, content -> string#2{} }

    p1 = p2;

    ++p2->counter
    --p1->counter
    because p1->counter goes to zero:
    delete helper_obj#1
    delete string#1

    result:
    p2 -> helper_obj#2{ counter = 2, content -> string#2{} }
    p1 -> helper_obj#2{ counter = 2, content -> string#2{} }

    cout<<"p2 use_count = "<<p2.use_count()<<endl;
    cout<<"second p1 use_count = "<<p1.use_count()<<endl;


    The helper objects cause additional allocations and more importantly an
    additional indirection at every access to the stored strings. But they
    are required to store arbitrary objects in shared_ptr instances.


    Marcel
     
    Marcel Müller, Feb 3, 2014
    #5
  6. somenath

    cuzdav Guest


    Perhaps it useful to have it explicitly pointed out: the reference count is associated with the shared object, not with the shared pointers.

    Initial:
    p1 refers to object1, whose count==1
    p2 refers to object2, whose count==1

    Change p1 to refer to what p2 refers to

    p1=p2

    (Notice, when p1 changes what it points to, the refcount
    goes down on the old object, and up on the new one. Afterall,
    p1 no longer points to the old object so its refcount must
    change!)

    Result:
    object1 destroyed (no references)
    p2 still refers to object2, whose count==2
    p1 also refers to object2, whose count==2


    Notice that p1 and p2 both end up referring to the same underlying
    object, so they both report the same reference count.


    Chris
     
    cuzdav, Feb 5, 2014
    #6
  7. somenath

    Stuart Guest

    Am 03.02.14 02:12, schrieb somenath:
    (this posting contains some ASCII art which will look quite bad if
    viewed with proportional fonts).

    The situation just before the assignment "p1 = p2;" looks like this:
    Each shared_pointer does not point to the object directly but to an
    intermediate object which contains the reference count. Both pointers p1
    and p2 are the only shared_pointers pointing to their strings, so the
    reference counts have both the value 1.


    ,''''''''`.
    | refcnt 1|
    ,_______ `. | | _______________
    | P1 |------=: |.......... . | |
    L______| - | | `. |"SSSSSSSSSS" |
    | ptr |----------'|_______________|
    |_________J ,'
    -'


    ,''''''''`.
    | refcnt 1|
    ,_______ `. | | _______________
    | P2 |------=: |.......... . | |
    L______| - | | `. |"" |
    | ptr |----------'|_______________|
    |_________J ,'
    -'


    In the assignment statement, p1 will decrement the ref-count of the
    intermediate object, since p1 no longer points to the intermediate
    object of string "SSSSSSSSSS". Instead it will point to the intermediate
    object of p2:


    ,''''''''`.
    | refcnt 0|
    ,_______ | | _______________
    | P1 | |.......... . | |
    L______| | | `. |"SSSSSSSSSS" |
    `. | ptr |----------'|_______________|
    `. |_________J ,'
    `. -'
    \ |
    `.|
    ---- ,''''''''`.
    | refcnt 2|
    ,_______ `. | | _______________
    | P2 |------=: |.......... . | |
    L______| - | | `. |"" |
    | ptr |----------'|_______________|
    |_________J ,'
    -'

    Now the assigment statement will figure out that the reference count of
    the intermediate object of "SSSSSSSSSS" has become zero, so it can
    delete the intermediate object and the string "SSSSSSSSSS".

    Sorry, if I did not use the proper vocabulary. The intermediate object
    is an implementation details, although I doubt whether there is an
    implementation that differs from this scheme.

    Regards,
    Stuart
     
    Stuart, Feb 6, 2014
    #7
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.