an easier copy-assignment operator implement? - c++

In C++ Primer 5th,13.2.1 Class That Act Like Values,the author make a class that act like a value, which means each object have its own copy of the resource that the class manage.The class is below, It is a very simple class, just contain a pointer to a string, and an int, member function are just default constructor and copy-control member.
class HasPtr{
public:
HasPtr(const std::string &s = std::string()) :ps(new std::string(s)), i(0){}
HasPtr(const HasPtr &p) :ps(new std::string(*p.ps)), i(p.i){}
HasPtr &operator=(const HasPtr &rhs);
~HasPtr(){ delete ps; }
private:
std::string *ps;
int i;
};
Below is the implement of the operator= given by the book
HasPtr &HasPtr::operator=(const HasPtr &rhs){
auto newp = new std::string(*rhs.ps);
delete ps;
ps = newp;
i = *rhs.i;
return *this;
}
This is good, but i think that we can use the following implement which can avoid delete pointer and allocate new memory.
HasPtr &HasPtr::operator=(const HasPtr &rhs){
*ps = *rhs.ps;
i = rhs.i;
return *this;
}
I test my code works, even self-assignment.However, is there any problem with this code?

No, there is no problem in your code.
*ps
is itself a value type, so you can assign it directly. If you were making changes to improve the code, you might want to go further and change ps into a std::string instead of a std::string*. Then, you could eliminate the need for new and delete from the HasPtr class.
If
*ps
were instead a raw pointer to memory that class HasPtr manages, you would have to write code such as in the example from the book.

In your original example, you had copied the contents of *ps to itself, which is OK. However as the comment to you suggested, this may not always be the case.
As to other solutions, here is one (maybe something like this will be explained later in your book):
#include <algorithm>
//...
HasPtr &HasPtr::operator=(const HasPtr &rhs)
{
HasPtr temp(rhs);
std::swap(ps, temp.ps);
std::swap(i, temp.i);
return *this;
}
This is the copy/swap idiom. A temporary is created from rhs, and the internals of the temporary are swapped out with this's members. The temporary dies, at the end, taking along with it this's former values.
Note that there is no explicit memory allocation, as you're relying on the copy constructor to create the temporary copy initially. This means that this technique requires a working copy constructor, and a working destructor. Once you have those in place, an assignment operator is very simple if done this way, as there is no real "grunt work" coding to do -- the copy constructor would handle it.
So is this easier than the book's version? Depends on how you look at it -- the copy/swap can be used in almost every situation, where you basically only need to start off with working implementations of two of the three functions described in the "rule of 3", namely the copy constructor and destructor. The assignment operator just comes along for the ride by taking advantage of the other two functions, plus perform simple swaps.

Related

Why can't I use a copy constructor inside of my assignment operator?

Title kind of hits the mark.
// copy constructor
IntList::IntList(const IntList& source){
Node* currentNode = source.first;
first = nullptr;
while(currentNode){
append(currentNode->info);
currentNode = currentNode->next;
}
}
//Assignment operator should copy the list from the source
//to this list, deleting/replacing any existing nodes
IntList& IntList::operator=(const IntList& source){
this -> ~IntList();
this = new IntList(source);
return *this;
}
Error:
intlist.cpp:26:30: error: lvalue required as left operand of assignment
this = new IntList(source);
The destructor properly deletes each node in the linked list. Technically "this" would be whatever linked list was passed in with the assignment operator. Isn't that treated as an object that can be set?
The short answer is that you can't assign this. (this is the pointer to the current object it cannot be "relocated", it is a "const" pointer for all purposes).
What you mean is something like:
new(this) IntList(source);
Which is called "placement" new:
But you don't need to learn about this because all this is bad anyway. Keep reading...
Yours is an old and bad design pattern to implement assignment as destruction + construction.
These days, unless there is a better shortcut (that depends on you specific design) we implement swap (also useful) and then operator= from copy(construct)+swap.
(Besides any explict call to the destructor smells bad.)
IntList& IntList::operator=(IntList const& source){
IntList tmp(source)
this->swap(tmp);
return *this;
}
The main point is not only more elegant code, ultimately a side effect is that you are writing exception safe code by default. (think of what happens if the new object cannot be constructed in your original code: the *this object is left in an invalid "destructed" state.)
See the series of talks: https://www.youtube.com/watch?v=W7fIy_54y-w
This is really advanced now and it gets weird (as discussed with #PaulMcKenzie).
It turns out that (I think for some quirks only in C++14), that you can omit the temporary and transfer that into the argument itself passed by value:
IntList& IntList::operator=(IntList source) noexcept{
return swap(source);
}
Although it can be obscure at first.
It is less code
it is noexcept: it can't fail!. The operation call can fail but not inside the function
this provides move assignment if there a move constructor, so you don't need to write a separate move assignment (even less code).
(Incidentally, I think this shows why member swap should return *this)
This opens an intriguing question: if I have copy constructor and swap, shouldn't the compiler implement operator= automatically for me? (not the old-C operator= member-by-member, but one that knows about the semantics of the class implemented like shown). IntList& operator=(IntList) = default;
Finally, don't miss the forest for the tree.
You wanted to implement assignment without writing new code, assuming that your copy constructor was good and that is all you got.
However this is still not always optimal!
For containers copy construction usually involves a) (unconditional) allocation and b) a sequence of "uninitialized copies" (ultimately a form of placement new).
Both a) and b) are slower than A) no-allocation and B) a sequence of assignment respectively.
This means that, if for some reason you know that you don't need to allocate (e.g. because the source "fits" in your allocated memory already), and that you can assign element by element (faster than new-place element by element) you might be in good shape to get a more optimal solution by writing a custom operator= that takes into account all this.
If your copy constructor and destructor for IntList are working properly, the easiest way to implement the assignment operator is to use the copy/swap idiom:
#include <algorithm>
//...
IntList& IntList::operator=(const IntList& source)
{
if (&source != this)
{
IntList temp(source);
std::swap(temp.first, first);
// add swaps for each member
//...
} // <-- This is where the old contents will be destroyed,
// when `temp` goes out of scope. No need for this->~IntList();
return *this;
}
In your code, doing this:
this->~IntList();
is not necessary, and probably is wrong anyway. Code like that is reserved for usage with placement-new, and your code doesn't show any usage of placement-new.
A common mistake is to call the assignment operator from the copy constructor. Take the Array class for example. Inside the copy you might think it's simple to write

Copy Constructor classes

Here is my piece of code, please help me understand how to get the copy constructor correct. My class has no copy constructor written for it
test(cosnt test&obj){
////////something
}
But yet when I attempt to make a copy such as test t2(t1); it appears to be copied correctly! Can you please explain to me the reason that the copy seems to work even without an explicit copy constructor?
#include <iostream>
using namespace std;
class test{
int *ptr;
public:
test(int t=0){
ptr=new int;
*ptr=t;
}
void increment(){
(*ptr)++;
}
void display(){
cout<<*ptr<<endl;
}
};
int main(){
test t1;
t1.display();
t1.increment();
t1.display();
test t2(t1);//copy constructor works fine!
t2.display();
t1.increment();
t1.display();
t2.display();
}
C++ is so amazing that when you don't define a copy constructor, move constructor, copy assignment and move assignment for a class, the compiler will have to define them for you (the Standard says so). Of course you can delete them via:
func() = delete;
So for example, if you want to delete the implicit copy constructor in your example you would declare:
test(const test&) = delete;
and as you can see your code won't no longer compile.
The behavior of the implicit copy constructor is the one you would expect: it will copy construct each member of the class to the the other object. In this case it will copy construct the pointer value (not the pointed value), effectively make the two object share the same pointer.
Now, your program is leaking memory right? You have called new but no delete. Let's say you want to clean your resources up by inserting:
delete ptr;
in the destructor (it's a simplified version, of course you would need to define at least a proper move assignment and move constructor). You know what will happen? A nice and beautiful runtime error, telling you that the pointer you are trying to free has not been allocated. The reason why that is, is that both your objects (t1 and t2) destructors a will be called and they will both delete the same pointer. The first correctly and the second erroneously.
For this reason a Rule of three (now Rule of Five) has been established in the C++ community. But you know what? There's even a better rule which is called Rule of Zero. To sums it up (but you should really read about it) it says: don't do RAII yourself. I'd suggest you to follow the latter.
Now, let's discuss a little a bit of new. I'm sure you know this, but I'm here to prevent future damages: you don't need to use new at all in C++. Not anymore, in most cases.
Most of the things pointers once did (optional parameter, pass array by pointer, copyable reference, etc..) has now been "deprecated" (not in the literal sense: they are still there) in favor of sexier approaches (namely boost:optional/std::optional, std::array/std::vector, std::reference_wrapper). And even when all those fails to give you what you need, you can still use std::shared_ptr and std::unique_ptr.
So, please, don't use naked new pointers. Thanks.
If you did not define the copy constructor yourself then the compiler implicitly defines it instead of you. This copy constructor makes member-wise copies of class data members.
Relative to your example of code the implicitly defined copy constructor will copy data member ptr. As the result two or more objects can refer to the same memory.
Your class also needs destructor and the copy assignment operator.
These three specail functions can look the following way for your class
test( const test &rhs ) : ptr( new int( *rhs.ptr ) )
{
}
test & operator =( const test &rhs )
{
if ( this != &rhs )
{
int tmp = new int( *rhs.ptr );
delete ptr;
ptr = tmp;
}
return ( *this );
}
~test()
{
delete ptr;
}
You did not define a copy constructor. Like #jrok said, the compiler-generated default copy constructor only does shallow member-wise copy.
Your copy constructor can look something like:
public:
test(const test& t)
{
ptr = new int;
*ptr = *t.ptr;
}
BTW, you might want to define a destrcutor too to prevent memory leak.
~test()
{
delete ptr;
}

Is it a good practice to point to a new address on free store when dynamicly allocating?

Below is an exercise from C++ Primer 5th Edition:
Exercise 13.22: Assume that we want HasPtr to behave like a value.
That is, each object should have its own copy of the string to which
the objects point. We’ll show the definitions of the copy-control
members in the next section. However, you already know everything you
need to know to implement these members. Write the HasPtr copy
constructor and copy-assignment operator before reading on.(Page 511)
Codes for Class HasPtr:
class HasPtr
{
public:
//! default constructor
HasPtr(const std::string &s = std::string()):
ps(new std::string(s)), i(0) { }
//! copy constructor
HasPtr(const HasPtr& hp) : ps(new std::string(*hp.ps)), i(hp.i) { }
HasPtr&
operator = (const HasPtr& hp);
~HasPtr()
{
delete ps;
}
private:
std::string *ps;
int i;
};
My code for this copy-assignment operator:
HasPtr&
HasPtr::operator = (const HasPtr &hp)
{
delete ps;
ps = new std::string(*hp.ps);
i = hp.i;
return *this;
}
Codes presented in the following section from this book:
HasPtr&
HasPtr::operator = (const HasPtr &rhs)
{
auto newp = new string(*rhs.ps); // copy the underlying string
delete ps; // free the old memory
ps = newp; // copy data from rhs into this object
i = rhs.i;
return *this; // return this object
}
By executing step by step, I found a slight difference between the two codes. In my code it doesn't change the address to which ps points, whereas the code from the book makes ps point to a new address. I'm wondering whether this subtle difference has any significant meaning? Should I always change the pointer to a new address in similar situations? why?
Your code has a problem with self-assignment and with exceptions: assume that the memory allocation throws a std::bad_alloc exception. When coding, you should always assume that memory allocations can go wrong although the actually rarely do. In the code
delete ps;
ps = new std::string(*hp.ps);
ps would point to stale member when the second line of code throws an exception. Incidentally, if you end up self-assigning the object, you actually delete the memory of the only before accessing it. Thus, it is a good idea to first copy the content of the right hand side, then put things into place, and finally release resource.
As it happens, these are exactly the operations of
the copy constructor
a swap() operation you generally want for any type holding resources
the destructor
The way to leverage these three operation is known as copy and swap idiom:
T& T::operator=(T other) {
this->swap(other);
return *this;
}
Functionally I can only see one difference.
delete ps;
ps = new std::string(*hp.ps);
If memory is low, the call to new std::string might possibly throw an exception. In your case, ps still has the address of the old deleted string -- so it is malformed. If you recover from the exception somebody might dereference ps and bad things will happen.
auto newp = new string(*rhs.ps); // copy the underlying string
delete ps; // free the old memory
ps = newp; // copy data from rhs into this object
In the text book code, ps is not deleted until after the new string is allocated. On exception, ps still points to a valid string so you do not have a malformed object.
How much of a problem that is depends on a few different things, but it is generally better practice to avoid any chance of a malformed object.
Your version is unsafe for self assignment.
delete ps;
ps = new std::string(*hp.ps);
Here,if doing a self assignment, you may be deleting the ps in both the source and destination, making its use in the new statement wrong (tho it may deceptively work on most occasions).
You can assign the value of the new directly into your member variable, but you can't just arbitrarily delete ps, before knowing if you need it.
If you tested for self assignment eg.if (this!=&hp) before doing your code, then it would be better, but still not ideal (see exception safety comments elsewhere).
There are actually two issues in your code:
Self-assignment
Exception safety
You normally want your member functions to provide strong exception guarantee, i.e., when assignment fails (in your case, it can be in operator new or the copy-constructor of string), the program state does not change.
I think a modern practice is to provide a swap function and make the assignment call the copy constructor. Something like:
void HasPtr::swap(HasPtr& rhs)
{
std::swap(this.ps, rhs.ps);
std::swap(this.i, rhs.i);
}
HasPtr(const HasPtr& rhs)
{
ps = new string(*rhs.ps);
i = rhs.i;
}
HasPtr& operator=(const HasPtr& rhs)
{
HasPtr temp(rhs);
this.swap(temp);
return *this;
}

Implementing the copy constructor in terms of operator=

If the operator= is properly defined, is it OK to use the following as copy constructor?
MyClass::MyClass(MyClass const &_copy)
{
*this = _copy;
}
If all members of MyClass have a default constructor, yes.
Note that usually it is the other way around:
class MyClass
{
public:
MyClass(MyClass const&); // Implemented
void swap(MyClass&) throw(); // Implemented
MyClass& operator=(MyClass rhs) { rhs.swap(*this); return *this; }
};
We pass by value in operator= so that the copy constructor gets called. Note that everything is exception safe, since swap is guaranteed not to throw (you have to ensure this in your implementation).
EDIT, as requested, about the call-by-value stuff: The operator= could be written as
MyClass& MyClass::operator=(MyClass const& rhs)
{
MyClass tmp(rhs);
tmp.swap(*this);
return *this;
}
C++ students are usually told to pass class instances by reference because the copy constructor gets called if they are passed by value. In our case, we have to copy rhs anyway, so passing by value is fine.
Thus, the operator= (first version, call by value) reads:
Make a copy of rhs (via the copy constructor, automatically called)
Swap its contents with *this
Return *this and let rhs (which contains the old value) be destroyed at method exit.
Now, we have an extra bonus with this call-by-value. If the object being passed to operator= (or any function which gets its arguments by value) is a temporary object, the compiler can (and usually does) make no copy at all. This is called copy elision.
Therefore, if rhs is temporary, no copy is made. We are left with:
Swap this and rhs contents
Destroy rhs
So passing by value is in this case more efficient than passing by reference.
It is more advisable to implement operator= in terms of an exception safe copy constructor. See Example 4. in this from Herb Sutter for an explanation of the technique and why it's a good idea.
http://www.gotw.ca/gotw/059.htm
This implementation implies that the default constructors for all the data members (and base classes) are available and accessible from MyClass, because they will be called first, before making the assignment. Even in this case, having this extra call for the constructors might be expensive (depending on the content of the class).
I would still stick to separate implementation of the copy constructor through initialization list, even if it means writing more code.
Another thing: This implementation might have side effects (e.g. if you have dynamically allocated members).
While the end result is the same, the members are first default initialized, only copied after that.
With 'expensive' members, you better copy-construct with an initializer list.
struct C {
ExpensiveType member;
C( const C& other ): member(other.member) {}
};
};
I would say this is not okay if MyClass allocates memory or is mutable.
yes.
personally, if your class doesn't have pointers though I'd not overload the equal operator or write the copy constructor and let the compiler do it for you; it will implement a shallow copy and you'll know for sure that all member data is copied, whereas if you overload the = op; and then add a data member and then forget to update the overload you'll have a problem.
#Alexandre - I am not sure about passing by value in assignment operator. What is the advantage you will get by calling copy constructor there? Is this going to fasten the assignment operator?
P.S. I don't know how to write comments. Or may be I am not allowed to write comments.
It is technically OK, if you have a working assignment operator (copy operator).
However, you should prefer copy-and-swap because:
Exception safety is easier with copy-swap
Most logical separation of concerns:
The copy-ctor is about allocating the resources it needs (to copy the other stuff).
The swap function is (mostly) only about exchanging internal "handles" and doesn't need to do resource (de)allocation
The destructor is about resource deallocation
Copy-and-swap naturally combines these three function in the assignment/copy operator

Class basic operators

Is it necessary to have a copy constructor, destructor and operator= in a class that have only static data member, no pointer
class myClass{
int dm;
public:
myClass(){
dm = 1;
}
~myClass(){ } // Is this line usefull ?
myClass(const myClass& myObj){ // and that operator?
this->dm = myObj.dm;
}
myClass& operator=(const myClass& myObj){ // and that one?
if(this != &myObj){
this->dm = myObj.dm;
}
return *this;
}
};
I read that the compiler build one for us, so it is better to not have one (when we add a data member we have to update the operators)
If you can't think of anything for the destructor to do, you almost certainly don't need to define it, with the exception of a virtual destructor in a base class. And if you don't need a destructor, you almost certainly don't need to define the copy constructor and assignment operator, and should not do so, as it's easy to get them wrong.
No. The compiler will automatically copy-construct, destruct, and assign all data members automatically. This means that if you have a class where they're all provided, you don't need to write these. I think it's better design to write operators on a per-member basis anyway.
The compiler would provide those itself. By default the copy constructor / assignment operator would copy / assign all members (and base parts of the object if the class is derived), and the destructor does nothing (invokes the destructor of all members / bases, but this is something you can't change even if you provide your own - that happens after the body of the user-defined destructor is exited).
So in this case it is completely unnecessary. All you can achieve is to introduce bugs in the code (e.g you add another member, but forget to update the copy constructor to copy that as well).
myClass& operator=(const myClass& myObj){ // and that one?
if(this != &myObj){
this->dm = myObj.dm;
}
return *this;
}
I don't think you should take "check for self-assignment" dogmatically. Does anything bad happen when you assign an int to itself (even indirectly)?
int a = 10;
int& ref = a;
a = ref;
Avoiding self-assignment is only relevant if the assignment operator first destroys the resources currently held and then creates new ones from the object on the right-hand side. Then it would be a disaster to find out that you have actually indirectly destroyed the right-hand object too.
But even in that case, it would be better to first create the copy of the right-hand side and then destroy the contents of the left-hand side, in which case self-assignment is no problem (except potentially inefficient). For a good way to implement this, Google for copy and swap idiom.