Copy Assignment Operator

closed account (Sw07fSEw)
I'm reading C++ Primer and grinding through the section on copy constructors. I looked up the solution to one of the problems, but I don't quite understand why a couple lines of code are there. The github solution is below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <string>

class HasPtr {
public:
    HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }
    HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { }
    HasPtr& operator=(const HasPtr &hp) {
        std::string *new_ps = new std::string(*hp.ps);
        delete ps;
        ps = new_ps;
        i = hp.i;
        return *this;
    }
    ~HasPtr() {
        delete ps;
    }
private:
    std::string *ps;
    int i;
};


I'm confused by the statement in line 8. I understand that ps must be deleted after the default constructor is called because it now points to a new std::string. What is the difference between these two pieces of code?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
HasPtr& operator=(const HasPtr &hp) { //copy assignment operator=
    std::string *new_ps = new std::string(*hp.ps);
    delete ps;
    ps = new_ps;
    i = hp.i;
    return *this;
}

vs

HasPtr& operator=(const HasPtr &hp) { //copy assignment operator=
    delete ps;
    ps = new std::string(*hp.ps); 
    i = hp.i;
    return *this;
}


Why would it be advantageous to create a pointer (line 2) and assign its value to the member ps (line 4) instead of just assigning it a new copy of *hp.ps (line 13) above?
I THINK your answer is, as Stroustrup indicates in Programming Principles and Programming (2E) with regards to a user-created vector and copy assignment:

"When implementing the assignment, you could consider simplifying the code by freeing the memory for the old elements before creating the copy, but it is usually a very good idea not to throw away information before you know that you can replace it. Also, if you did that, strange things would happen if you assigned [the object] to itself."
closed account (Sw07fSEw)
Thanks. I ended up reading several more pages into the book and it explained this exact scenario. I'll try to give an explanation in case anyone has the same question.

The issue is that is the assignment operator won't work correctly if the object is assigned to itself:

1
2
HasPtr ptr; 
ptr = ptr;


If the object is assigned to itself, the parameter hp and this in the copy assignment operator both point to the same location in memory.

For the sake of this example, let's define the constructor slightly differently, with text this time...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class HasPtr {
public:
    HasPtr(const std::string &s = std::string("Hello World")) : ps(new std::string(s)), i(0) { }
    HasPtr& operator=(const HasPtr &hp) { // copy assignment operator
        std::string *new_ps = new std::string(*hp.ps);
        delete ps;
        this->ps = new_ps;
        i = hp.i;
        return *this;
    }
private:
    std::string *ps;
    int i;
};


If the object is assigned to itself, this and hp will point to the same location in memory. Therefore this.ps == hp.ps evaluates to true. By deleting ps in line 6, which is also equivalent to this->ps, we are also implicitly deleting hp.ps because they both refer to the same object. Therefore, after calling delete on ps, *hp.ps is now undefined because it was just implicitly deleted.

So if we used this code...

1
2
3
4
5
6
HasPtr& operator=(const HasPtr &hp) { //copy assignment operator=
    delete ps; // memory is freed at location that both ps AND hp.ps are pointing too
    ps = new std::string(*hp.ps); //*hp.ps is undefined because hp.ps was just deleted
    i = hp.i;
    return *this;
}


...We can see how allocating and assigning a new string to ps is dangerous; *hp.ps is undefined and therefore so will *ps.
Last edited on
Topic archived. No new replies allowed.