Stelios Papamichail
Stelios Papamichail

Reputation: 1290

Understanding and using a copy assignment constructor

I'm trying to understand how the copy assignment constructor works in c++. I've only worked with java so i'm really out of my waters here. I've read and seen that it's a good practice to return a reference but i don't get how i should do that. I wrote this small program to test the concept:

main.cpp:

#include <iostream>
#include "test.h"

using namespace std;

int main() {
    Test t1,t2;
    t1.setAge(10);
    t1.setId('a');
    t2.setAge(20);
    t2.setId('b');

    cout << "T2 (before) : " << t2.getAge() << t2.getID() << "\n";

    t2 = t1; // calls assignment operator, same as t2.operator=(t1)

    cout << "T2 (assignment operator called) : " << t2.getAge() << t2.getID() << "\n";

    Test t3 = t1; // copy constr, same as Test t3(t1)

    cout << "T3 (copy constructor using T1) : " << t3.getAge() << t3.getID() << "\n";

    return 1;
}

test.h:

class Test {
    int age;
    char id;

    public:
        Test(){};
        Test(const Test& t); // copy
        Test& operator=(const Test& obj); // copy assign
        ~Test();
        void setAge(int a);
        void setId(char i);
        int getAge() const {return age;};
        char getID() const {return id;};
};

test.cpp:

#include "test.h"

void Test::setAge(int a) {
    age = a;
}

void Test::setId(char i) {
    id = i;
}

Test::Test(const Test& t) {
    age = t.getAge();
    id = t.getID();
}

Test& Test::operator=(const Test& t) {

}

Test::~Test() {};

I can't seem to understand what i should be putting inside operator=(). I've seen people returning *this but that from what i read is just a reference to the object itself (on the left of the =), right? I then thought about returning a copy of the const Test& t object but then there would be no point to using this constructor right? What do i return and why?

Upvotes: 4

Views: 812

Answers (3)

scohe001
scohe001

Reputation: 15446

We return a reference from the assignment operator so we can do some cool tricks like @SomeWittyUsername shows.

The object we want to return a reference to is the one who the operator is being called on, or this. So--like you've heard--you'll want to return *this.

So your assignment operator will probably look like:

Test& Test::operator=(const Test& t) {
    age = t.getAge();
    id = t.getID();
    return *this;
}

You may note that this looks strikingly similar to your copy-constructor. In more complicated classes, the assignment operator will do all the work of the copy-constructor, but in addition it'll have to safely remove any values the class was already storing.

Since this is a pretty simple class, we have nothing we need to safely remove. We can just re-assign both of the members. So this will be almost exactly the same as the copy-constructor.

Which means that we can actually simplify your constructor to just use the operator!

Test::Test(const Test& t) {
    *this = t;
}

Again, while this works for your simple class, in production code with more complicated classes, we'll usually want to use initialization lists for our constructors (read here for more):

Test::Test(const Test& t) : age(t.getAge()), id(t.getId()) { }

Upvotes: 2

R Sahu
R Sahu

Reputation: 206667

I've read and seen that it's a good practice to return a reference but i don't get how i should do that.

How

Add

return *this;

as the last line in the function.

Test& Test::operator=(const Test& t) {
   ...
   return *this;
}

Why

As to the question of why you should return *this, the answer is that it is idiomatic.

For fundamental types, you can use things like:

int i;
i = 10;
i = someFunction();

You can use them in a chain operation.

int j = i = someFunction();

You can use them in a conditional.

if ( (i = someFunction()) != 0 ) { /* Do something */ }

You can use them in a function call.

foo((i = someFunction());

They work because i = ... evaluates to a reference to i. It's idiomatic to keep that semantic even for user defined types. You should be able to use:

Test a;
Test b;

b = a = someFunctionThatReturnsTest();

if ( (a = omeFunctionThatReturnsTest()).getAge() > 20 ) { /* Do something */ }

But Then

More importantly, you should avoid writing a destructor, a copy constructor, and a copy assignment operator for the posted class. The compiler created implementations will be sufficient for Test.

Upvotes: 5

SomeWittyUsername
SomeWittyUsername

Reputation: 18368

Returning reference to the original object is needed for support of nested operations. Consider

a = b = c

Upvotes: 3

Related Questions