3
#include <iostream>
#include <string>
#include <vector>
using namespace std;

class HasPtr{
 public:
    //constructor accepts a string
    HasPtr(const string &s = string()) : ps(new string(s)), i(0), use(new size_t(1)) {}  
    //copy constructor
    HasPtr(const HasPtr &h) : ps(h.ps), i(h.i), use(h.use) { ++*use; }
    //copy assignment operator 
    HasPtr &operator=(const HasPtr &h)
    {
        ++*h.use;
        if(--*use == 0)
        {
            delete ps;
            delete use;
        }
        ps = h.ps;
        i = h.i;
        use = h.use;

        return *this;
    }
    ~HasPtr()
    {
        if(--*use == 0)
        {
            delete ps;
            delete use;
        }
    }

//private:
    string *ps;
    int i;

    size_t *use;
};


int main()
{
    HasPtr h("hi mom");
    HasPtr h2 = h;
    HasPtr h3("hi dad");
    h2 = h3;

    cout << "h: " << *h.ps << endl;
    cout << "h2: " << *h2.ps << endl;
    cout << "h3: " << *h3.ps << endl;
}

The output is:

h: hi mom
h2: hi dad
h3: hi dad

I think the output should be:

h: hi dad
h2: hi dad
h3: hi dad

The reason why I think the output should like above:

h is "hi mom", h2 shares h1, so h2 is "hi mom", h3 is "hi dad", and I think h2 = h3 changed h1 either because h2 shares h1, but it's not.

What am I doing wrong?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
XIAODI
  • 109
  • 5
  • 2
    `What am I doing wrong?`: Using pointers. If you use objects all this goes away. Also there is already a reference counted smart pointer `std::shared_ptr` – Martin York Jul 27 '15 at 21:32
  • 2
    `h2 = h3` does not affect `h1` at all. You're not modifying *what's pointed to*, but *the pointers stored in `h2`*. Compare to: http://coliru.stacked-crooked.com/a/c380c6c80e66a393 – dyp Jul 27 '15 at 21:33
  • 1
    For future reference, you may want to read about the [copy and swap idiom](https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom) and [the Big Three](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). As stated by @LokiAstari, you probably also just want to use the builtin `std::shared_ptr`. – Alyssa Haroldsen Jul 27 '15 at 21:35
  • 1
    h2 shares h1 up until the point where you assign to h2. This is classic reference counting - the semantics are as if you were dealing with simple value objects, but copies are deferred until they are really needed. A nice idea, but in practice not a win, especially if you are using multiple threads. – Alan Stokes Jul 27 '15 at 21:37

2 Answers2

3

Here's whats going on:

HasPtr h("hi mom");
HasPtr h2 = h;

h.ps ---> "hi mom"  <--- h2.ps

Then with the next statement, you change h2 but that doesn't affect h:

HasPtr h3("hi dad");
h2 = h3;

h2.ps ----> "hi dad" <--- h3.ps
 |
XXX changed
 |
"hi mom" <--- h.ps

The pointers are different instances that point to the same address. Example:

int a = 5, c = 1; 
int *p1 = &a, *p2 = &a;

Changing p1 (ie p1 = &c) won't affect p2.

yizzlez
  • 8,757
  • 4
  • 29
  • 44
  • still don't get it. h2 has the same pointer as h1. If h2 changes, why doesn't h1 changes? – XIAODI Jul 27 '15 at 21:44
  • @XIAODI The pointers are different instances that point to the same address. Example: `int a = 5; int *p1 = &a, *p2 = &a`. Changing `p1` (ie `p1 = &c`) won't affect `p2` – yizzlez Jul 27 '15 at 21:46
2

That it would be more clear let's consider a simplified example

int x = 10;
int y = 20;

int *h = &x;
int *h2 = h;

Now h and h2 point to x. You can check this outputting the object pointed to by the pointers. For example

std::cout << "*h = " << *h << ", *h2 = " << *h2 << std::endl;

Then you are using the third pointer

int *h3 = &y;
h2 = h3;

Now h2 and h3 point to y. h was not changed. You also can check this

std::cout << "*h2 = " << *h2 << ", *h3 = " << *h3 << std::endl;
std::cout << "*h = " << *h << std::endl;

The same occurs with your objects in statements

HasPtr h("hi mom");
HasPtr h2 = h;
HasPtr h3("hi dad");
h2 = h3;

only corresponding pointers (ps) are wrapped in the objects of the class.

Here is a program that demonstrates the explanation

#include <iostream>
#include <string>


int main()
{
    std::string hi_mom( "hi mom" );
    std::string hi_dad( "hi dad" );

    std::string *h  = &hi_mom;
    std::string *h2 = h;

    std::cout << "*h = " << *h << ", *h2 = " << *h2 << std::endl;

    std::string *h3 = &hi_dad;
    h2 = h3;

    std::cout << "*h2 = " << *h2 << ", *h3 = " << *h3 << std::endl;
    std::cout << "*h = " << *h << std::endl;
}        

Its output is

*h = hi mom, *h2 = hi mom
*h2 = hi dad, *h3 = hi dad
*h = hi mom
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Thank you, could you give me an example of corresponding pointers? – XIAODI Jul 27 '15 at 21:54
  • @XIAOD "Corresponding pointers" are the data member ps declared like string *ps; of the corresponding objects h, h2 and h3. In fact the class is simply a wrapper around this pointer, In my example I removed the wrapper leaving only the pointers themselves and naming them the same way as the names of the objects.. – Vlad from Moscow Jul 27 '15 at 21:57