The copy constructor and assignment operator - c++

If I override operator= will the copy constructor automatically use the new operator? Similarly, if I define a copy constructor, will operator= automatically 'inherit' the behavior from the copy constructor?

No, they are different operators.
The copy constructor is for creating a new object. It copies an existing object to a newly constructed object.The copy constructor is used to initialize a new instance from an old
instance. It is not necessarily called when passing variables by value into functions
or as return values out of functions.
The assignment operator is to deal with an already existing object. The assignment operator is used to change an existing instance to have
the same values as the rvalue, which means that the instance has to be
destroyed and re-initialized if it has internal dynamic memory.
Useful link :
Copy Constructors, Assignment Operators, and More
Copy constructor and = operator overload in C++: is a common function possible?

No. Unless you define a copy ctor, a default will be generated (if needed). Unless you define an operator=, a default will be generated (if needed). They do not use each other, and you can change them independently.

No. They are different objects.
If your concern is code duplication between copy constructor and assignment operator, consider the following idiom, named copy and swap :
struct MyClass
{
MyClass(const MyClass&); // Implement copy logic here
void swap(MyClass&) throw(); // Implement a lightweight swap here (eg. swap pointers)
MyClass& operator=(MyClass x)
{
x.swap(*this);
return *this;
}
};
This way, the operator= will use the copy constructor to build a new object, which will get exchanged with *this and released (with the old this inside) at function exit.

No.
And definitely have a look at the rule of three
(or rule of five when taking rvalues into account)

Consider the following C++ program.
Note: My "Vector" class not the one from the standard library.
My "Vector" class interface:
#include <iostream>
class Vector {
private:
double* elem; // elem points to an array of sz doubles
int sz;
public:
Vector(int s); // constructor: acquire resources
~Vector() { delete[] elem; } // destructor: release resources
Vector(const Vector& a); // copy constructor
Vector& operator=(const Vector& a); // copy assignment operator
double& operator[](int i){ return elem[i]; };
int size() const {return sz;};
};
My "Vector" class members implementation:
Vector::Vector(int s) // non-default constructor
{
std::cout << "non-default constructor"<<std::endl;
elem = {new double[s]};
sz =s;
for (int i=0; i!=s; ++i) // initialize elements
elem[i]=0;
}
Vector::Vector(const Vector& a) // copy constructor
:elem{new double[a.sz]},
sz{a.sz}
{
std::cout << "copy constructor"<<std::endl;
for (int i=0; i!=sz; ++i) // copy elements
elem[i] = a.elem[i];
}
Vector& Vector::operator=(const Vector& a) // copy assignment operator
{
std::cout << "copy assignment operator"<<std::endl;
double* p = new double[a.sz];
for (int i=0; i!=a.sz; ++i)
p[i] = a.elem[i];
delete[] elem; // delete old elements
elem = p;
sz = a.sz;
return *this;
}
int main(){
Vector v1(1);
v1[0] = 1024; // call non-default constructor
Vector v2 = v1; // call copy constructor !!!!
v2[0] = 1025;
std::cout << "v2[0]=" << v2[0] << std::endl;
Vector v3{10}; // call non-default constructor
std::cout << "v3[0]=" << v3[0] << std::endl;
v3 = v2; // call copy assignment operator !!!!
std::cout << "v3[0]=" << v3[0] << std::endl;
}
Then, the program output:
non-default constructor
copy constructor
v2[0]=1025
non-default constructor
v3[0]=0
copy assignment operator
v3[0]=1025
To wrap up:
Vector v2 = v1; lead to call copy constructor.
v3 = v2; lead to call copy assignment operator.
In case 2, Object v3 already exists (We have done: Vector v3{10};). There are two obvious differences between copy constructor and copy assignment operator.
copy constructor NO NEED to delete old elements, it just copy construct a new object. (as it Vector v2)
copy constructor NO NEED to return the this pointer.(Furthermore, all the constructor does not return a value).

No, they are not the same operator.

Related

Copy constructor replace Move constructor?

I learn move semantics from link
I have a class
class Holder
{
public:
Holder(int size) // Constructor
{
m_data = new int[size];
m_size = size;
}
~Holder() // Destructor
{
delete[] m_data;
}
Holder(const Holder& other)
{
cout << "copy constructor" << endl;
m_data = new int[other.m_size];
memcpy(m_data, other.m_data, sizeof(int));
m_size = other.m_size;
}
Holder &operator=(const Holder& other)
{
if (this == &other)
return *this;
delete[]m_data;
m_data = new int[other.m_size];
memcpy(m_data, other.m_data, sizeof(int));
m_size = other.m_size;
return *this;
}
private:
int* m_data;
size_t m_size;
};
This class have copy constructor like :
Holder(const Holder& other)
{
cout << "copy constructor" << endl;
m_data = new int[other.m_size];
memcpy(m_data, other.m_data, sizeof(int));
m_size = other.m_size;
}
And then move constructor have implemented like :
Holder(Holder&& other) // <-- rvalue reference in input
{
m_data = other.m_data; // (1)
m_size = other.m_size;
other.m_data = nullptr; // (2)
other.m_size = 0;
}
I have a question :Why we do not implement copy constructor like below :
Holder( Holder& other)
{
m_data = other.m_data;
m_size = other.m_size;
other.m_data = nullptr;
other.m_size = 0;
}
Could you please show me why this way doesn't use ?
Thanks
Your implementation of that "copy constructor" is semantically something in between a copy constructor and a move constructor, which will be confusing and then dangerous. If your intent is to move always and never being able to copy an object, then you can force the class as being not copyable with this:
Holder(const Holder& other) = delete;
Holder& operator=( const Holder& ) = delete;
Even if you follow the rules for the non generation by the compiler of the default copy constructor and the default assignment, explicitly deleting these methods is more clear.
I guess for safety reasons. Although what you described is allowed (and correct when one knows what one is doing), by doing that one could end up with an object which looks full but it is actually empty.
In general move constructor is more useful for using it with temporary values like:
Holder a_function(...){...}
Which then can be used for construction like:
Holder object(a_function(...));
Or to avoid reassigning/copying large amount of data/memory when doing something like:
Holder object(Holder(100));
But specially for this case which there is no default constructor (so in general every object should be full after construction) , by having the copy constructor as what you suggested (similar to move constructor) and then by doing:
Holder object1(100);
Holder object2(object1);
One would end up with object1 which looks as a normal object but it is empty. Hence it could become source for bug/s later.
Although it is obvious, I should add that there would be no problem with the destruction of the object1. It is just that, using it in its life time, not being aware of its emptiness if there are not some type of safe guards in place (boundary checks) will most likely cause illegal memory access.
I have a question :Why we do not implement copy constructor like below
:
Holder( Holder& other)
{
m_data = other.m_data;
m_size = other.m_size;
other.m_data = nullptr;
other.m_size = 0;
}
Could you please show me why this way doesn't use ?
Above code is not copying the state from other but moving the state/owned resources of other to current object being created.
from Move constructors
Move constructors typically "steal" the resources held by the argument
(e.g. pointers to dynamically-allocated objects, file descriptors, TCP
sockets, I/O streams, running threads, etc.) rather than make copies
of them, and leave the argument in some valid but otherwise
indeterminate state.
Holder A;
Holder B(std::move(A));
// B is created by calling move constructor
// Resources held by A are transferred to B (ref1)
Holder C;
Holder D(C);
//C is created by calling copy constructor
//state or resources of C, D are same and C can be used after this
//Object C usable
ref1

Copy constructor is called instead of move constructor - why?

I have this code, taken from here by the way http://www.cplusplus.com/doc/tutorial/classes2/
// move constructor/assignment
#include <iostream>
#include <string>
#include <utility>
using namespace std;
class Example6
{
string* ptr;
public:
Example6(const string& str) :
ptr(new string(str))
{
cout << "DONT MOVE " << '\n';
}
~Example6()
{
delete ptr;
}
// move constructor
Example6(Example6&& x) :
ptr(x.ptr)
{
cout << "MOVE " << '\n';
x.ptr = nullptr;
}
// move assignment
Example6& operator=(Example6&& x)
{
delete ptr;
ptr = x.ptr;
x.ptr = nullptr;
return *this;
}
// access content:
const string& content() const
{
return *ptr;
}
// addition:
Example6 operator+(const Example6& rhs)
{
return Example6(content() + rhs.content());
}
};
int main()
{
Example6 foo("Exam");
Example6 bar = Example6("ple"); // move-construction
foo = foo + bar; // move-assignment
cout << "foo's content: " << foo.content() << '\n';
return 0;
}
I only added output in constructor to see which is being called. To my surprise it is always the first one, copy constructor. Why does it happen? I did some research and found some info about elision. Is it somehow possible to prevent it and always call move constructor?
Also, as a side note, as I said this code is from cplusplus.com. However, I read about move semantics in some other places and I wonder if this move constructor here is done right. Shouldn't it call
ptr(move(x.ptr))
instead of just
ptr(x.ptr)
The way I understand this, if we use the second option, then we are calling copy constructor of string, instead of move, because x is rvalue reference that has a name, so it is really lvalue and we need to use move to cast it to be rvalue. Do i miss something, or is it really tutorial's mistake?
Btw, adding move doesn't solve my first problem.
So anything with a name is an lvalue.
An rvalue reference with a name is an lvalue.
An rvalue reference will bind to rvalues, but it itself is an lvalue.
So x in ptr(x.ptr) is an rvalue reference, but it has a name, so it is an lvalue.
To treat it as an rvalue, you need to do ptr( std::move(x).ptr ).
Of course, this is mostly useless, as moving a ptr does nothing as ptr is a dumb raw pointer.
You should be following the rule of 0 here.
class Example6 {
std::unique_ptr<string> ptr;
public:
Example6 (string str) : ptr(std::make_unique<string>(std::move(str))) {cout << "DONT MOVE " << '\n';}
Example6():Example6("") {}
~Example6 () = default;
// move constructor
Example6 (Example6&& x) = default;
// move assignment
Example6& operator= (Example6&& x) = default;
// access content:
const string& content() const {
if (!ptr) *this=Example6{};
return *ptr;
}
// addition:
Example6 operator+(const Example6& rhs) {
return Example6(content()+rhs.content());
}
};
because business logic and lifetime management don't belong intermixed in the same class.
While we are at it:
// addition:
Example6& operator+=(const Example6& rhs) & {
if (!ptr) *this = Example6{};
*ptr += rhs.content();
return *this;
}
// addition:
friend Example6 operator+(Example6 lhs, const Example6& rhs) {
lhs += rhs;
return lhs;
}
Copy constructor is called ... - why?
The premise of your question is faulty: The copy constructor is not called. In fact, the class is not copyable.
The first constructor is a converting constructor from std::string. The converting constructor is called because Example6 objects are initialised with a string argument. Once in each of these expressions:
Example6 foo("Exam")
Example6("ple")
Example6(content() + rhs.content()
... instead of move constructor
There are a few copy-initialisations by move in the program. However, all of them can be elided by the compiler.
Is it somehow possible to prevent it and always call move constructor?
There are a few mistakes that can prevent copy elision. For example, if you wrote the addition operator like this:
return std::move(Example6(content()+rhs.content()));
The compiler would fail to elide the move and probably tell you about it if you're lucky:
warning: moving a temporary object prevents copy elision
Shouldn't it call
ptr(move(x.ptr))
instead of just
ptr(x.ptr)
There's no need. Moving a pointer is exactly the same as copying a pointer. Same holds for all fundamental types.
The way I understand this, if we use the second option, then we are calling copy constructor of string, instead of move
ptr is not a string. It is a pointer to a string. Copying a pointer does nothing to the pointed object.
PS. The example program is quite bad quality. There should never be owning bare pointers in C++.
I can say your class does not have a copy constructor.
Because copy ctor parameter have to be const and reference
class Example6{
public:
Example6(const Example6 &r);
};

moving temporary value using rvalue reference

I am trying to lean move semantics and I wrote this example. I would like to move a temporary r-value into an object on stack.
class MemoryPage
{
public:
size_t size;
MemoryPage():size(0){
}
MemoryPage& operator= (MemoryPage&& mp_){
std::cout << "2" <<std::endl;
size = mp_.size;
return *this;
}
};
MemoryPage getMemPage()
{
MemoryPage mp;
mp.size = 4;
return mp;
}
int main() {
MemoryPage mp;
mp = getMemPage();
std::cout << mp.size;
return 0;
}
I get this error at the return of getMemPage():
error: use of deleted function 'constexpr MemoryPage::MemoryPage(const MemoryPage&)'
The copy constructor is:
[...] defined as deleted if any of the following conditions are true:
T has a user-defined move constructor or move assignment operator
In order to solve the immediate problem, you simply provide a copy constructor, i.e.:
MemoryPage(const MemoryPage&) { }
However, as noted in the comments, it's a good idea to consult Rule-of-Three becomes Rule-of-Five with C++11? . In particular, this paragraph summarizes what issues you may run into if you neglect to provide any of the special member functions:
Note that:
move constructor and move assignment operator won't be generated for a class that explicitly declares any of the other special member
functions
copy constructor and copy assignment operator won't be generated for a class that explicitly declares a move constructor or move assignment
operator
a class with a explicitly declared destructor and implicitly defined copy constructor or implicitly defined copy assignment operator is
considered deprecated.
formatted for readability
Therefore it is good practice when writing a class that deals with memory management to provide all five special member functions, i.e.:
class C {
C(const C&) = default;
C(C&&) = default;
C& operator=(const C&) & = default;
C& operator=(C&&) & = default;
virtual ~C() { }
};

C++ : Implementing copy constructor and copy assignment operator

After reading about copy constructors and copy assignment operators in C++, I tried to create a simple example. Though the below snippet apparently works, I am not sure whether I am implementing the copy constructor and copy assignment operator the right way. Could you please point out if there are any mistakes/improvements or a better example to understand the relevant concepts.
class Foobase
{
int bInt;
public:
Foobase() {}
Foobase(int b) { bInt = b;}
int GetValue() { return bInt;}
int SetValue(const int& val) { bInt = val; }
};
class Foobar
{
int var;
Foobase *base;
public:
Foobar(){}
Foobar(int v)
{
var = v;
base = new Foobase(v * -1);
}
//Copy constructor
Foobar(const Foobar& foo)
{
var = foo.var;
base = new Foobase(foo.GetBaseValue());
}
//Copy assignemnt operator
Foobar& operator= (const Foobar& other)
{
if (this != &other) // prevent self-assignment
{
var = other.var;
base = new Foobase(other.GetBaseValue());
}
return *this;
}
~Foobar()
{
delete base;
}
void SetValue(int val)
{
var = val;
}
void SetBaseValue(const int& val)
{
base->SetValue(val);
}
int GetBaseValue() const
{
return(base->GetValue());
}
void Print()
{
cout<<"Foobar Value: "<<var<<endl;
cout<<"Foobase Value: "<<base->GetValue()<<endl;
}
};
int main()
{
Foobar f(10);
Foobar g(f); //calls copy constructor
Foobar h = f; //calls copy constructor
Foobar i;
i = f;
f.SetBaseValue(12);
f.SetValue(2);
Foobar j = f = z; //copy constructor for j but assignment operator for f
z.SetBaseValue(777);
z.SetValue(77);
return 1;
}
Your copy assignment operator is implemented incorrectly. The object being assigned to leaks the object its base points to.
Your default constructor is also incorrect: it leaves both base and var uninitialized, so there is no way to know whether either is valid and in the destructor, when you call delete base;, Bad Things Happen.
The easiest way to implement the copy constructor and copy assignment operator and to know that you have done so correctly is to use the Copy-and-Swap idiom.
Only Foobar needs a custom copy constructor, assignment operator and destructor. Foobase doesn't need one because the default behaviour the compiler gives is good enough.
In the case of Foobar you have a leak in the assignment operator. You can easily fix it by freeing the object before allocating it, and that should be good enough. But if you ever add a second pointer member to Foobar you will see that that's when things get complicated. Now, if you have an exception while allocating the second pointer you need to clean up properly the first pointer you allocated, to avoid corruption or leaks. And things get more complicated than that in a polynomial manner as you add more pointer members.
Instead, what you want to do is implement the assignment operator in terms of the copy constructor. Then, you should implement the copy-constructor in terms of a non-throwing swap function. Read about the Copy & Swap idiom for details.
Also, the default constructor of Foobar doesn't default-initialize the members. That's bad, because it's not what the user would expect. The member pointer points at an arbitrary address and the int has an arbitrary value. Now if you use the object the constructor created you are very near Undefined Behaviour Land.
I have a very simple patch for you:
class Foobar
{
int var;
std::unique_ptr<FooBase> base;
...
That should get you started.
The bottom line is:
Don't call delete in your code (Experts see point 2)
Don't call delete in your code (you know better...)

segmentation fault in overloading operator =

I just got a seg fault in overloading the assignment operator for a class FeatureRandomCounts, which has _rects as its pointer member pointing to an array of FeatureCount and size rhs._dim, and whose other date members are non-pointers:
FeatureRandomCounts & FeatureRandomCounts::operator=(const FeatureRandomCounts &rhs)
{
if (_rects) delete [] _rects;
*this = rhs; // segment fault
_rects = new FeatureCount [rhs._dim];
for (int i = 0; i < rhs._dim; i++)
{
_rects[i]=rhs._rects[i];
}
return *this;
}
Does someone have some clue? Thanks and regards!
*this = rhs;
calls operator=(), which is the function you are writing. Cue infinite recursion, stack overflow, crash.
Also, if you used a std::vector rather than a C-style array, you probably would not need to implement operator=() at all.
As mentioned, you have infinite recursion; however, to add to that, here's a foolproof way to implement op=:
struct T {
T(T const& other);
T& operator=(T copy) {
swap(*this, copy);
return *this;
}
friend void swap(T& a, T& b);
};
Write a correct copy ctor and swap, and exception safety and all edge cases are handled for you!
The copy parameter is passed by value and then changed. Any resources which the current instance must destroy are handled when copy is destroyed. This follows current recommendations and handles self-assignment cleanly.
#include <algorithm>
#include <iostream>
struct ConcreteExample {
int* p;
std::string s;
ConcreteExample(int n, char const* s) : p(new int(n)), s(s) {}
ConcreteExample(ConcreteExample const& other)
: p(new int(*other.p)), s(other.s) {}
~ConcreteExample() { delete p; }
ConcreteExample& operator=(ConcreteExample copy) {
swap(*this, copy);
return *this;
}
friend void swap(ConcreteExample& a, ConcreteExample& b) {
using std::swap;
//using boost::swap; // if available
swap(a.p, b.p); // uses ADL (when p has a different type), the whole reason
swap(a.s, b.s); // this 'method' is not really a member (so it can be used
// the same way)
}
};
int main() {
ConcreteExample a (3, "a"), b (5, "b");
std::cout << a.s << *a.p << ' ' << b.s << *b.p << '\n';
a = b;
std::cout << a.s << *a.p << ' ' << b.s << *b.p << '\n';
return 0;
}
Notice it works with either manually managed members (p) or RAII/SBRM-style members (s).
*this = rhs; // segment fault
This is definitively not the way to do it. You call = recursively, not calling the built in assignment operator. Assign variables one by one. Don't be lazy.
The following line:
*this = rhs; // segment fault
will recursively call your operator=() function resulting in a stack overflow.
You should probably replace it with straight-forward assignments of the various member fields.
As Neil said, using something like std::vector<> will remove much of the responsibility away from your code. If for whatever reason you can't or don't want to use std::vector<>, you might also want to consider using the 'swap idiom' for your assignment operator. This will make the function exception safe (if the allocation of the memory for FeatureCount array fails and throws an exception, the original object that's being assigned to will be left unchanged). Something like the following:
void FeatureRandomCounts::swap( FeatureRandomCounts& other)
{
FeatureCount* tmp_rects = other._rects;
int tmp_dim = other._dim; // or whatever type _dim is
// similarly for other members of FeatureRandomCounts...
// now copy the other contents to
this->_rects = other._rects;
this->_dim = other._dim;
// assign other members of rhs to lhs
other._rects = tmp_rects;
other._dim = tmp_dim;
// etc.
return;
}
Now your assignment can look like:
FeatureRandomCounts & FeatureRandomCounts::operator=(const FeatureRandomCounts &rhs)
{
FeatureRandomCounts tmp( rhs); // make a copy
tmp.swap( *this); // swap the contents of the copy and *this
return *this;
// the contents of tmp (which has the old
// stuff that was in *this) gets destructed
}
Note that you need a proper copy constructor for this to work, but given the Big 3 rule you already need a proper copy ctor.