copy and assignment operators

I need someone to explain what how to write a copy constructor.
obj(const obj &);//copy constructor
I'm not giving you the class code, no need for you to write the code for me, just tell me what I need to do and explain why...


Also, I believe this would be called an assignmet operator:

void operator=(const obj &); // operator=

How do you write one of these? And what would be the difference between the two? Hopefully this is something standard so you know what I am talking about... cheers!
Last edited on
Copy constructors and overloaded assignment operators are very similar, and fall under the category of copy control in a class.

There's two scenarios (at least that I can think of off the top of my head) when a copy constructor would be called. One, is obviously when you use it like a constructor. For example, if I have a class called Book, you could use the copy constructor to initialize one object to the same state as another:
1
2
Book book1();    // calling a default constructor
Book book2(book1);    // calling a copy constructor 

The other, and more frequent case, is that the copy constructor is implicitly called whenever you pass an object to a function by value, like in the following case:
1
2
3
4
void func(Book book);

Book book1;
func(book1);    // the copy constructor is called here 

A copy constructor is synthesized automatically for you, unless you define your own. The problem is that the synthesized copy constructor performs a member by member copying process. In most scenarios this is okay, but when your classes have members that are pointers (and especially if you are dynamically allocating memory) this can be terrible. For example, if the book class looks as follows:
1
2
3
4
5
6
7
8
class Book {
public:
	Book();		// default constructor
	Book(const Book&);	// copy constructor
	std::string *getText() const { return text; }
private:
	std::string *text;	// random pointer to the text of the book
};

then the default copy constructor will not suffice. If we were to perform an operating like this:
1
2
Book book1;
Book book2(book1);

Now the pointer book2.text points to the same slot in memory as book1.text! In other words, any changes to book2.text also change book1.text. What's more, using the same func(Book book) function we defined above, if we called the function in the following manner func(book1), even though we passed book1 by value, any changes to book1.text in the function will permanently change book1.text (as in, even outside of the function).

So how can we fix this? By defining our own copy constructor. In this case, it would make sense to define it as follows:
1
2
3
4
5
Book::Book(const Book &bk)
{
	std::string txt = *(bk.getText());
	text = &txt;
}

Voila! Now when performing the operation book2(book1), book2 has the same text as book1 but in a different slot in memory. Now any subsequent changes to book2.text will leave book1.text untarnished. And if we call our function func with book2, any changes to book2.text in the function won't change book2.text outside of the function.

It is especially important to implement your own copy constructor whenever your class has dynamically allocated members. If we didn't, then whenever we deleted one object, the other would be left with a dangling pointer.

Note: You can often get out of having to write a copy constructor by passing objects of your class to functions by reference.


Okay, now on to the overloaded assignment operator. The overloaded assignment operator is called whenever you... use the assignment operator with members of your class. For example:
1
2
3
Book  book1, book2;
book1 = book2;    // calls the overloaded assignment operator
book1.operator=(book2);    // note that this is equivalent to the above statement 

Defining an overloaded assignment operator is very similar to defining a copy constructor. Using our book class, one would probably define the overloaded assignment operator as:
1
2
3
4
5
6
7
8
Book& Book::operator=(const Book &bk)
{
        if (this != &rhs) {
	    std::string txt = *(bk.getText());
	    text = &txt;
        }
	return *this;
}

The only real changes worth noting are that this function returns a reference to this, so that you can chain assignment expressions (i.e. book1 = book2 = book3), and that it prevents self-assignment.

So in the end, our book class looks like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Book {
public:
	Book();		// default constructor
        // copy control
	Book(const Book&);	  // copy constructor
	Book& operator=(const Book&);    // overloaded assignment operator

	std::string *getText() const { return text; }
private:
	std::string *text;	// random pointer to the text of the book
};

Book::Book(const Book &bk)
{
	std::string txt = *(bk.getText());
	text = &txt;
}

Book& Book::operator=(const Book &bk)
{
        if (this != &rhs) {
	    std::string txt = *(bk.getText());
	    text = &txt;
        }
	return *this;
}

(except I didn't define a constructor or any of that jazz, but it's not really needed in this example)

Also, the copy constructor you posted above (void operator=(const obj &);) is in error because it doesn't return a reference to this which it should. It also isn't a member function in your example, and it is a compile time error to not have it be so.

Hope this helps :)
Last edited on

ascii (852)

The other, and more frequent case, is that the copy constructor is implicitly called whenever you pass an object to a function by value, like in the following case:
void func(Book book);

Book book1;
func(book1); // the copy constructor is called here



It is not valid in general case. Consider the following class

1
2
3
4
5
6
7
8
struct Book
{
   template <typename T>
   Book( Book<T> & ) {}
};

Book book1;
func( book1 );

In the last statement the template constructor will be called instead of the implicitly defined by the compiler copy constructor.
In that class you defined your own copy constructor, so the compiler doesn't even generate one. Or maybe I'm not understanding what you're saying.

ascii (853)

In that class you defined your own copy constructor, so the compiler doesn't even generate one. Or maybe I'm not understanding what you're saying.


You are wrong. Copy constructor is a non-template constructor. So in example I have shown the compiler will implicitly define a copy constructor. But it will not be called to pass an argument to the function parameter.
Even if you expliciitly define copy constructor which has as parameter const reference it wiil not be called. The template constructor will be called. Only if your argument will be a const object in that case the copy constructor will be called
Last edited on
Okay, fair point. But the example you posted above seems somewhat unrealistic.

And for the record, (853) isn't part of my name, it's a post count (I'm assuming you didn't name yourself vlad from moscow (316)).

Yours truly,
ascii (854)
Last edited on
Topic archived. No new replies allowed.