Overloading the assignment operator or use the default one? - c++

If I have
Class A {
int x;
};
Class B {
vector<A> y;
//...
};
And I want to overload the assignment operator = in B. Will using the default = operator be enough? Will it just use the = operator of vector on y member?
EDIT: say I wanted to implement something like that myself, so that b = k will work if both b and k are of type B. would I need to explicitly call the vector destructor to free the y member of b, inside the implementation?
How will it look like?
B& B::operator=(const B& b) {
if (this == &b) {
return *this;
}
y = b.y;
return *this;
}
Will the original vector this->y destructed here? why?

Will using the default = operator be enough?
Yes, it's sufficient, unless you have any resources that need special treatment or parameters.
Will it just use the = operator of vector on y member?
Yes, the generated default assignment operator/copy constructor will call any assignment operators/copy constructors available for member variables automatically.
B& B::operator=(const B& b) {
if (this == &b) {
return *this;
}
y = b.y;
return *this;
}
Will the original vector this->y destructed here? why?
Yes, it will be "destructed", since the operator=() definition of vector<A> implies to do so.
It's not really the destructor function is called, but the implementation of the assignment operator does imply the same behaviour as there would be a new instance constructed, and all of the contained member destructors will be called when the vector is cleared.

If you don't have special cases (like owning pointers, or unique_ptr), you can just not-define it and use the default one. In your case it should be fine.
In case of owning pointers, you would suddenly have two objects pointing to the same data in memory, which might not be the behaviour you want (same goes for shared_ptr.
unique_ptr can't be copied, so the default assignment operator won't work in this case and you'd have to write an overload.

Related

Should the assignment operator allocate new memory for data members or reuse existing memory?

I have a question regarding the assignment operator (apologies if this has already been answered in a different post).
As I understand the assignment operator, it is suppose to assign the value of one object to another, e.g.
class A {
public:
A();
A& operator=(const A& rhs)
{
b = rhs.b;
return *this;
}
private:
B b;
};
Now, if the class contains pointers, it seems to be common practice to allocate new memory in the assignment operator. This makes the assignment operator more complicated since one needs to be more careful, e.g.
class A {
public:
A() : b(0)
{
b = new B;
}
A& operator=(const A& rhs)
{
if (this != &rhs) {
B* b1 = 0;
try {
b1 = new B(*rhs.b);
}
catch {
delete b1;
throw;
}
delete b;
b = b1;
}
return *this;
}
private:
B* b;
};
So my question is: Why not assign the value of the existing object pointed to, i.e. reuse the memory by calling B's assignment operator as one would do if b was not a pointer (see my first example)? Then the assignment would look like this
class A {
public:
A() : b(0)
{
b = new B;
}
A& operator=(const A& rhs)
{
*b = *rhs.b;
return *this;
}
private:
B* b;
};
This is much more simple and does exactly what I would expect from the assignment operator.
Moreover, I can think of at least one example where the more complicated assignment operator will get me into trouble: If class A has a member function which returns b
const B* A::GetB() const
{
return b;
}
then the following code will leave a dangling pointer
A a1, a2;
const B* my_b = a1.GetB();
a1 = a2; // this leaves my_b dangling!
Your comments are much appreciated. Thanks!
You should always reuse existing memory whenever possible. In you case, you should reuse the existing memory only if you are dead certain that b points to valid memory.
It depends on the exact scenario at hand really. Usually, it is a good practice to reuse the memory, but really, when you are trying to assign a differently sized internal array, etc, you might be better off with reallocating or delete/new.
Note that, the own-cope check at the beginning would still be desired to avoid A = A alike situation, however.
I can't think of any reason that you shouldn't try to do that. The examples that I have found where the memory is deleted is due to the fact that the type itself wasn't copyable. If you have a class attribute and it is a pointer to a copyable type then your suggestion should work conceptually (meaning that you only posted a partial snip so I am not saying that what is in your example is perfect). Some older examples show how it is done with char* and int* or other types of dynamic arrays. However, modern C++ programmers should be trying to use std::string and container classes. Now we also have smart pointers so that you can have shared pointers without having to worry about dangling references or worrying about which object is really the owner of it. Below is an example of what I mean. The example does use a template but copies as though the T is a c-array (not a container class) so deep copying has to be done as in your first example.
http://www.cplusplus.com/articles/y8hv0pDG/

C++ operator destroys input variables

This is probably just me being stupid somehow or the other, but I am relatively new to C++, so forgive me the idiocy. I'm trying to teach myself how to use operators. I've defined a very basic operator as follows:
Matrix::Matrix operator+(Matrix a1, Matrix a2) {
if(a1.rows != a2.rows || a1.cols != a2.cols) {
std::cout << "ERROR: Attempting to add matrices of non-equal size." << std::endl;
exit(6);
}
int i, j;
Matrix c(a1.rows, a1.cols);
for(i = 0; i < a1.rows; i++)
for(j = 0; j < a1.cols; j++)
c.val[i][j] = a1.val[i][j] + a2.val[i][j];
return c;
}
The class Matrix represents a matrix, and has a constructor that takes two ints as input (the number of rows and columns in the matrix, respectively), and creates a 2D array of doubles of the appropriate size (named val). This function works as supposed to in that the value for c is correct, but it also appears to destruct a1 and a2. That is, if I write
Matrix c = a + b;
It gives the right result, but a and b are no longer usable, and I get a glibc error at the end of the code claiming I am trying to destruct a and b when they have already been destructed. Why is this?
Your signature is wrong:
Matrix operator+(Matrix a1, Matrix a2)
it should be
Matrix operator+(const Matrix& a1, const Matrix& a2)
The reason it appears to destroy a1 and a2 is because, well, it is, since those are temporary copies created in the method scope.
If the original values are destroyed, you're probably violating the rule of three. When a1 and a2 are destroyed, the destructor gets called, and you're probably deleting pointers in the destructor. But since the default copy constructor does only a shallow copy, the copied a2 and a1 will delete the original memory.
Edit: Since there are split opinions about this, I'll extend my answer:
Assume:
struct A
{
int* x;
A() { x = new int; *x = 1; }
~A() { delete x; }
};
//option 1:
A operator + (A a1, A a2)
{
A a; return a; //whatever, we don't care about the return value
}
//option 2:
A operator + (const A& a1, const A& a2)
{
A a; return a; //again, we don't really care about the return value
}
In this first example, the copy constructor is not implemented.
A copy constructor is generated by the compiler. This copy constructor copies x into the new instance. So if you have:
A a;
A b = a;
assert( a.x == b.x );
Important note that the pointers are the same.
Calling option 1 will create copies inside operator +, because the values are passed by value:
A a;
A b;
a + b;
//will call:
A operator + (A a1, A a2)
// a1.x == a.x
// a2.x == n.x
When operator + exits, it will call delete x on objects a1 and a2, which will delete the memory that is also pointed to by a.x and b.x. That is why you get the memory corruption.
Calling option 2 however, since no new objects are created because you pass by reference, the memory will not be deleted upon function return.
However, this isn't the cleanest way to solve the issue. It solves this issue, but the underlying one is much more important, as Konrad Pointed out, and I have in my original answer (although haven't given it enough importance, I admit).
Now, the correct way of solving this is properly following the rule of three. That is, have an implementation for destructor, copy constructor and assignment operator:
struct A
{
int* x;
A() { x = new int; *x = 1; }
A(const A& other) //copy constructor
{
x = new int; // this.x now points to a different memory location than other.x
*x = other.(*x); //copy the value though
}
A& operator = (const A& other) //assignment operator
{
delete x; //clear the previous memory
x = new int;
*x = other.(*x); //same as before
}
~A() { delete x; }
};
With this new code, let's re-run the problematic option 1 from before:
A a;
A b;
a + b;
//will call:
A operator + (A a1, A a2)
// a1.x != a.x
// a2.x != n.x
Because the copies a1 and a2 now point to different memory locations, when they are destroyed, the original memory is intact and the original objects - a and b remain valid.
Phew! Hope this clears things up.
I’m assuming that you are allocating dynamic memory inside the Matrix class using new and didn’t implement a custom copy constructor, thus violating the rule of three.
The cause of the error would then be that the local copy of the Matrix instances reuses the dynamic memory of the argument instances, and their destructors free it at the end of the method.
The solution is very simple: Don’t use pointers. Instead, you could use a nested std::vector to store the data.
Furthermore, the head of the operator is mangled. Depending on where you declare the function, it must either look like this, if you define the operator inside the class:
ReturnType operator +(Arg1)
Or, if you define it outside the class, it needs to look like this:
ReturnType operator +(Arg1, Arg2)
Yours is a wild mix of both. I’m assuming that your operator definition should look as follows:
Matrix operator +(Matrix a, Matrix b) { … }
If your code compiles then this is either an error in the compiler or you are using a very weird class structure indeed. Furthermore, as Luchian pointed out, it’s more efficient to pass the arguments as const& rather than by value. However, you should first make your code run correctly.
As Luchian Grigore pointed out, your signature is wrong and should be:
Matrix operator+(const Matrix& a1, const Matrix& a2)
But even this signature should not itself spoil a and b. But because you are copying arguments, I have another suspicion.
Have you defined your own copy constructor? I think you may be deleting the old variables when you are copying their values to the operator's arguments.
Please share your copy constructor (and preferably also operator= and the two-argument constructor that you use in this operator)
Since the size of the matrix is given at runtime, I suppose that the Matrix class hold a pointer that is initialized with a dynamically allocated array that is deleted by the destructor.
In that case, the copy constructor and the assignment operator must also be definedin order to allocate the copies of what the a matrix holds.
Not doing that, the default copy and assignment will just reassign the pointers, letting you with two or more Matrix holding a same array. And when one is destroyed (thus deleting the array) the others remain dangling.

Making shallow copy inside assignment operator

I need to implement an assignment operator for a class with a lot of members which I don't want to assign manually. Can I first make a shallow memory copy and then perform the necessary initializations?
class C
{
public:
C &operator=(const C &rhs)
{
if (&rhs == this)
return *this;
memcpy(this, &rhs, sizeof(C));
Init(rhs);
return *this;
}
.........
};
Thanks.
No. Unless the object has a POD type, this is undefined behavior. And
a user defined assignment operator means that it's not a POD. And in
practice, it could fail for a number of reasons.
One possible solution is to define a nested POD type with the data
members, and simply assign it, e.g.:
class C
{
struct Data { /* ... */ };
Data myData;
public:
C& operator=( C const& other )
{
myData = other.myData;
return *this;
}
};
Of course, that means that you need to constantly refer to each member
as myData.x, rather than simply x.
Well you could, but all your copied pointer members(if any) will then point to the same object and if that object goes out of scope, You would be left with a dangling pointer for all other objects which refer to it.
You are trying to dereference a C++ reference using the * operator. Unless you have operator* defined for this class it's not gonna work.
I mean in line
memcpy(this, *rhs, sizeof(C));

Why should the assignment operator return a reference to the object?

I'm doing some revision of my C++, and I'm dealing with operator overloading at the minute, specifically the "="(assignment) operator. I was looking online and came across multiple topics discussing it. In my own notes, I have all my examples taken down as something like
class Foo
{
public:
int x;
int y;
void operator=(const Foo&);
};
void Foo::operator=(const Foo &rhs)
{
x = rhs.x;
y = rhs.y;
}
In all the references I found online, I noticed that the operator returns a reference to the source object.
Why is the correct way to return a reference to the object as opposed to the nothing at all?
The usual form returns a reference to the target object to allow assignment chaining. Otherwise, it wouldn't be possible to do:
Foo a, b, c;
// ...
a = b = c;
Still, keep in mind that getting right the assigment operator is tougher than it might seem.
The return type doesn't matter when you're just performing a single assignment in a statement like this:
x = y;
It starts to matter when you do this:
if ((x = y)) {
... and really matters when you do this:
x = y = z;
That's why you return the current object: to allow chaining assignments with the correct associativity. It's a good general practice.
Your assignment operator should always do these three things:
Take a const-reference input (const MyClass &rhs) as the right hand side of the assignment. The reason for this should be obvious, since we don't want to accidentally change that value; we only want to change what's on the left hand side.
Always return a reference to the newly altered left hand side, return *this. This is to allow operator chaining, e.g. a = b = c;.
Always check for self assignment (this == &rhs). This is especially important when your class does its own memory allocation.
MyClass& MyClass::operator=(const MyClass &rhs) {
// Check for self-assignment!
if (this == &rhs) // Same object?
return *this; // Yes, so skip assignment, and just return *this.
... // Deallocate, allocate new space, copy values, etc...
return *this; //Return self
}
When you overload an operator and use it, what really happens at compilation is this:
Foo a, b, c;
a = b;
//Compiler implicitly converts this call into the following function call:
a.operator=(b);
So you can see that the object b of type FOO is passed by value as argument to the object a's assignment function of the same type. Now consider this, what if you wanted to cascade assignment and do something like this:
a = b = c;
//This is what the compiler does to this statement:
a.operator=(b.operator=(c));
It would be efficient to pass the objects by reference as argument to the function call because we know that NOT doing that we pass by value which makes a copy inside a function of the object which takes time and space.
The statement 'b.operator=(c)' will execute first in this statement and it will return a reference to the object had we overloaded the operator to return a reference to the current object:
Foo &operator=(const Foo& rhs);
Now our statement:
a.operator=(b.operator=(c));
becomes:
a.operator(Foo &this);
Where 'this' is the reference to the object that was returned after the execution of 'b.operator=(c)'. Object's reference is being passed here as the argument and the compiler doesn't have to create a copy of the object that was returned.
Had we not made the function to return Foo object or its reference and had made it return void instead:
void operator=(const Foo& rhs);
The statement would've become something like:
a.operator=(void);
And this would've thrown compilation error.
TL;DR You return the object or the reference to the object to cascade(chain) assignment which is:
a = b = c;

Creating an assignment (=) operator for class in C++ [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Operator overloading
EDIT 2
I was using insert(...) incorrectly, I didn't actually need a '=' operator. Sorry to waste peoples' time. I have voted to close.. 2 votes remain. Please vote.
EDIT
The reason I want an '=' operator is so I can use the insert(...) function on a vector of Derivation objects. At the moment my compiler says:
/usr/include/c++/4.2.1/bits/stl_algobase.h:283: error: no match for 'operator=' in '* __result = * __first'
I have created '==' and '<' operators for my own classes before but I'm struggling to create an '=' operator. My class looks like this (ignore the silly variable names):
class Derivation {
public:
string rc;
ImplementationChoice Y;
vector<Derivation> X;
vector<string> D;
vector<string> C;
vector<Player> P, O;
vector<Attack> B;
// various functions
// ...
};
and I want to know what I need to put in
// What do '=' return? An object of the class right?
Derivation& operator=(const Derivation &d) const {
// something....
}
Many thanks.
First, an assignment operator probably should not be const--
Second, assignment operators usually return a non-const reference to the object that was assigned a value (*this)
You don't need one. The compiler-generated one will do just fine.
first remove the const ...
then if you really need a copy operator, do something like that and add your own logic (so that it doesn't do just exactly what would be done with the compiler generated copy operator) :
Derivation& operator=(const Derivation& other) {
this->rc = other.rc;
this->Y = other.Y;
this->X = other.X;
this->D = other.D;
this->C = other.C;
this->P = other.P;
this->O = other.O;
this->B = other.B;
// ...
return *this;
}
This is up to you, really. What do you need the operator to do? Do you want to return a reference, or do you want a copy?
EDIT: Please note that this was rhetorical. What you use this vector for will determine if you need a reference or a copy. For example, if the object your inserting is at any point going to go out of scope before being removed from the vector, you'll want a copy. If not, and you want the original object to be effected when you change the instance in the vector, you'll want a reference. Hope that helps a bit.
The standard way to implement an assignment operator is copy-and-swap.
This has the advantages of being the most simple way to make an assignment operator that is correct in the face of exceptions and self-assignment. It also defines the assignment operation in terms of the copy-constructor, thus reducing the number of places where your code needs to be changed if you add extra members to the class.
Anyway - here is what it looks like in your case:
class Derivation {
public:
string rc;
ImplementationChoice Y;
vector<Derivation> X;
vector<string> D;
vector<string> C;
vector<Player> P, O;
vector<Attack> B;
//You need to add a swap function to your class
void swap(Derivation& o) {
rc.swap(o.rc);
Y.swap(o.Y);//Assuming ImplementationChoice has a swap function (it should!)
X.swap(o.X);
D.swap(o.D);
C.swap(o.C);
P.swap(o.P);
O.swap(o.O);
B.swap(o.B);
}
Derivation& operator=(Derivation const& o) {
Derivation copy(o);
copy.swap(*this);
return *this;
}
// various functions
// ...
};
to overload assignment operator you should do this
Derivation& operator=(const Derivation &d) {
// something....
return *this
}
This will allow you to do something like.
Deviation a, b, c;
//something
c = b = a;
Since - #jalf has not put up an answer, here it is :)
Derivation& operator=(const Derivation &d) {
// something....
return *this;
}
You need to return a reference to this instance. this is a keyword which holds a pointer to the instance on which the operator is acting.