I wanted to ask a question about operator overloading in C++.
I am a beginner in C++ and have been learning OOP.
Consider the following overloaded copy assignment operator:
class Base {
private:
int value;
public:
Base () : value {0}
{
cout << "Base No-args constructor" << endl;
}
Base (int x) : value {x} {
cout << "Base (int) overloaded constructor" << endl;
}
Base (const Base &other) : value {other.value} {
cout << "Base copy constructor" << endl;
}
Base & operator = (const Base &rhs) {
cout << "Base operator=" << endl;
if (this == &rhs)
return *this;
value = rhs.value;
return *this;
}
~ Base () {cout << "Base Destructor" << endl;}
};
I wanted to clarify two points.
How does this copy assignment operator work?
Does the reference before the operator keyword need a parameter name?
I wanted to give my interpretation on (1).
If I have the following code in my main():
Base b {100}; // overloaded constructor
Base b1 {b}; // copy constructor
b = b1; // copy assignment
What I think happens is that the no args constructor is called for a1, evidently because no arguments are passed into the construction of the object.
When a2 is initialised, a temporary copy of a1 is made and then the operator is evaluated with respect to the Base class, hence the Base overloaded copy assignment block is run, and the a1 object is returned via return *this to the reference a2.
To explain my thoughts on the second question,
I thought that all parameters need a name when a function or method is declared (I may of course be wrong).
If my overloaded copy assignment block was written hypothetically as:
Base &lhs operator = (const Base &rhs)
am I right in saying that lhs is referring to a2 but as we don't do anything with lhs due to the implied this parameter, we don't need to give a parameter name following the ampersand before the operator?
How does this copy assignment operator work?
Redacted:
Base& operator=(Base const& rhs) {
cout << "Base operator=\n";
value = rhs.value;
return *this;
}
The assignment operator is just a function call. In this particular class's case, the compiler will synthesize one that does the right thing, but if you want the cout side-effect for educational purposes you've done the right thing.
You could also call it in a method function style:
b.operator=(b1);
The b = b1; is just syntactic sugar for the above.
The assignment operator should be self-assignment safe. But general guidance is to make it safe without checking for the self-assignment case explicitly, which is "bad" because it optimizes for the pathological case.
In your implementation, it is safe without the self-assignment check.
I prefer specifying the qualifier in Base const& order rather than const Base& order, because the general rule is "the qualifier always binds to the thing to its immediate left", and the exception to the general rule is "...unless the qualifier comes first, in which case it then binds to the thing to its immediate right." That becomes a problem when people have the grasped the exception to the rule, but not the general rule, and then have troubles parsing Base*const* in their heads.
Does the reference before the operator keyword need a parameter name?
It's name is *this. It is a return type, not a parameter, so it does not have a parameter name.
Some people have troubles with "Why does C++ use this as a pointer to itself, rather than as a reference to itself."
It is a historical anomaly from the evolution of C++. Originally, there was a this pointer, and references had not been added to the language yet.
With 20/20 hindsight, this would have been a reference. But that ship has sailed.
In my own code, only to help make it more legible, I'll do:
auto& self = *this;
self[i] = 5;
...rather than the more confusing (imo)...
(*this)[i] = 5;
Related
In an already existing class of a project I am working on I encountered some strange piece of code: The assignment operator calls the copy constructor.
I added some code and now the assignment operator seems to cause trouble.
It is working fine though if I just use the assignment operator generated by the compiler instead. So I found a solution, but I'm still curious to find out the reason why this isn't working.
Since the original code is thousands of lines I created a simpler example for you to look at.
#include <iostream>
#include <vector>
class Example {
private:
int pValue;
public:
Example(int iValue=0)
{
pValue = iValue;
}
Example(const Example &eSource)
{
pValue = eSource.pValue;
}
Example operator= (const Example &eSource)
{
Example tmp(eSource);
return tmp;
}
int getValue()
{
return pValue;
}
};
int main ()
{
std::vector<Example> myvector;
for (int i=1; i<=8; i++) myvector.push_back(Example(i));
std::cout << "myvector contains:";
for (unsigned i=0; i<myvector.size(); ++i)
std::cout << ' ' << myvector[i].getValue();
std::cout << '\n';
myvector.erase (myvector.begin(),myvector.begin()+3);
std::cout << "myvector contains:";
for (unsigned i=0; i<myvector.size(); ++i)
std::cout << ' ' << myvector[i].getValue();
std::cout << '\n';
return 0;
}
The output is
myvector contains: 1 2 3 4 5
but it should be (an in fact is, if I just use the compiler-generated assignment operator)
myvector contains: 4 5 6 7 8
Your operator= does not do what everyone (including the standard library) thinks it should be doing. It doesn't modify *this at all - it just creates a new copy and returns it.
It's normal to re-use the copy constructor in the copy assignment operator using the copy-and-swap idiom:
Example& operator= (Example eSource)
{
swap(eSource);
return *this;
}
Notice how the operator takes its parameter by value. This means the copy-constructor will be called to construct the parameter, and you can then just swap with that copy, effectively assigning to *this.
Also note that it's expected from operator= to return by reference; when overloading operators, always follow the expected conventions. Even more, the standard actually requires the assignment operator of a CopyAssignable or MoveAssignable type to return a non-const reference (C++11 [moveassignable] and [copyassignable]); so to correctly use the class with the standard library, it has to comply.
Of course, it requires you to implement a swap() function in your class:
void swap(Example &other)
{
using std::swap;
swap(pValue, other.pValue);
}
The function should not raise exceptions (thanks #JamesKanze for mentioning this), no to compromise the exception safety of the operator=.
Also note that you should use the compiler-generated default constructors and assignment operators whenever you can; they can never get out of sync with the class's contents. In your case, there's no reason to provide the custom ones (but I assume the class is a simplified version for posting here).
The assignment operator you found is not correct. All it does is make a copy of eSource, but it's supposed to modify the object on which it is called.
The compiler-generated assignment operator for the class is equivalent to:
Example &operator= (const Example &eSource)
{
pValue = eSource.pValue;
return *this;
}
For this class there's no point implementing operator=, since the compiler-generated version basically cannot be improved on. But if you do implement it, that's the behaviour you want even if you write it differently.
[Alf will say return void, most C++ programmers will say return a reference. Regardless of what you return, the vital behaviour is an assignment to pValue of the value from eSource.pValue. Because that's what the copy assignment operator does: copy from the source to the destination.]
First of all, operator=() should return a reference:
Example& operator=(const Example& eSource)
{
pValue = eSource.pValue;
return *this;
}
Mind that your version returns a copy of tmp so in fact it performs two copies.
Second of all, in your class there's no need to even define custom assignment operator or copy constructor. The ones generated by compiler should be fine.
And third of all, you might be interested in copy-and-swap idiom: What is the copy-and-swap idiom?
Probably the most frequent correct implementation of operator=
will use the copy constructor; you don’t want to write the same
code twice. It will do something like:
Example& Example::operator=( Example const& other )
{
Example tmp( other );
swap( tmp );
return *this;
}
The key here is having a swap member function which swaps the
internal representation, while guaranteeing not to throw.
Just creating a temporary using the copy constructor is not
enough. And a correctly written assignment operator will always
return a reference, and will return *this.
I want to redirect my copy operator to my copy constructor. Where in the latter I implement the proper logic for copying/contructing a new class based on the old avaialble class.
However, how is the proper way to do this? I "think" this one is maybe leaking memory, but I don't know how to do it without passing a pointer:
MyClass& MyClass::operator=(const MyClass& a) {
MyClass* b = new MyClass(a);
return *b;
}
Is this OK? If is not, what would be the proper way? Should I change the body of the method or the prototype?
Thank you.
No, an operator= should set the current object attributes to be the same as the object assigned. Your method assigns a new object on the heap, returns it as a reference (essentially leaking it) and leaves the object the operator was called on completely unchanged.
You should implement a method called, for example, CopyFrom(), which assigns all the object's attributes to match those of the passed in object (deep copying any heap allocated pointers whose lifetime is managed by MyClass) and then call THAT from both your copy constructor and your operator=.
class MyClass
{
public:
MyClass( const MyClass& in )
{
CopyFrom( in );
}
MyClass& operator=( const MyClass& in )
{
CopyFrom( in );
return *this;
}
private:
void CopyFrom( const MyClass& in )
{
... copies in's attributes into self.
}
};
As a rule, a copy assignment operator should never create a copy. Rather, it should copy data into the existing object that it's called on (the left-hand side of the assignment). For example:
class MyClass
{
public:
MyClass & operator = (const MyClass & RHS)
{
// Copy data from RHS into 'this'
m_value = RHS.m_value;
return *this;
}
private:
int m_value;
};
In this case, defining your own copy constructor isn't necessary because the default (compiler-provided) one would work fine. It's just an illustration though.
Unfortunately you can't invoke the copy constructor on the existing object. The copy-swap pattern is an alternative, but it can be less efficient.
Unless you're storing pointers inside of MyClass, the correct copy assignment operator is the default-generated one. If, however you need to implement one, you can write it in terms of your copy-constructor via the copy-swap idiom:
MyClass& MyClass::operator = (MyClass const& a) {
MyClass temp(a); // Call the copy constructor
using std::swap;
swap(temp, *this);
return *this;
}
The reason for the using std::swap is to enable argument-dependent lookup. If you define your own swap function for MyClass, it will be called. Else std::swap will be used as a fallback. (EDIT: You do in fact need to implement a custom swap in this case, or else you will get infinite recursion. std::swap will use the assignment operator, which will call std::swap, which will call the...)
The reason that this idiom is well-liked is because std::swap is a no-throw function. If your copy-constructor were to throw an exception, then your object you're assigning to is still in a valid state.
The "proper way" is to implement the assignment operator like an assignment operator: modify the contents of the object on which the operator is being called and return a reference to it.
Your current implementation will result in a memory leak, AND doesn't do any assignment (which is the main point of the assignment operator).
If you only want to write the assignment code once, and your class doesn't allocate memory in the constructor, you could do this:
MyClass::MyClass(const MyClass& a) {
*this = a;
}
MyClass& MyClass::operator=(const MyClass& a) {
if (&a == this)
return *this;
// Do assignment
return *this;
}
But I wouldn't recommend it.
Your code is totally wrong (sorry)! The assignment operator does
not assign anything, but allocates a pointer to a MyClass object,
creating a memory leak. My advice: avoid pointers or use some
smart pointer (shared_ptr, unique_ptr), but that is just a side note.
Maybe this is helpful:
#include <iostream>
#include <limits>
class X
{
public:
X(std::size_t n)
: m_size(n), m_data(new int[n])
{
std::cout << "Construct" << std::endl;
}
~X()
{
std::cout << "Destruct" << std::endl;
delete [] m_data;
}
// Exception safe assignment.
// Note: I am passing by value to enable copy elision and
// move semantics.
X& operator = (X x) {
std::cout << "Assign" << std::endl;
x.swap(*this);
return *this;
}
void swap(X& x) {
std::swap(m_size, x.m_size);
std::swap(m_data, x.m_data);
}
std::size_t size() const { return m_size; }
private:
std::size_t m_size;
int* m_data;
};
int main()
{
X x(1);
try {
x = X(2);
// To provoke an exception:
std::size_t n = std::numeric_limits<std::size_t>::max();
x = X(n);
}
catch(...) {
std::cout << "Exception" << std::endl;
}
std::cout << "Size: " << x.size() << std::endl;
return 0;
}
If you absolutely want to implement the assignment operator by copy constructor, use the following:
MyClass& MyClass::operator=(const MyClass& o)
{
this->~MyClass(); // destroy current object
new(this) MyClass(o); // use the copy constructor
return *this;
}
I cannot think of any situation in which this would be the best thing to do (other answers describe ways of implementation that are better in some situations).
Maybe (just trying to make things up here) if MyClass contains hundreds of int/float fields, and several dynamically-allocated pointers?
Duplicating them in constructor and assignment operator is too tedious and error-prone
Having a copying function that both constructor and assignment operator call - not ideal, because pointers have to be set to NULL first
The code above - will work with no additional effort!
However, having bare (non-smart) pointers in your class is discouraged. If you have such a class, then you have far worse problems than non-working assignment operator - you have to refactor first, and the problem will go away, together with all other bugs.
I am confused over the differences between passing objects by reference and by value to functions of a particular class. If I pass objects by value, I know that the default copy constructor makes a member-by-member copy of the the object for use in the given function. However, if I am passing objects as a const reference for a class that requires deep copy, is the copy constructor still called? Say that I had a function
void debug(const MyClass& object1);
Would passing object1 call the copy constructor? Or is the object passed into the function directly without having a copy made? One more question - If I have a class called Fraction-
Fraction A(1,2); // 1 is this numerator, 2 the denominator
A = Fraction(2,3);
Does the aforementioned line call the default constructor to make a temporary object Fraction(2,3) and then the assignment operator?
Thanks.
Would passing object1 call the copy constructor?
No, it will not call the copy constructor since passed by reference
No copy is made in this case
A = Fraction(2,3);
Yes, it will call the constructor with two parameters (or default constructor if both parameters have default values), then call the copy assignment operator.
You can see the output from code below:
#include <iostream>
using namespace std;
class Fraction
{
public:
int denom;
int nominator;
Fraction(int d , int n ):denom(d), nominator(n)
{
cout << "call non-copy constructor" <<endl;
}
Fraction(const Fraction& rhs)
{
cout << "call copy constructor" <<endl;
denom = rhs.denom;
nominator = rhs.nominator;
}
const Fraction& operator=(const Fraction& rhs)
{
cout << "call copy assignment operator" << endl;
if (this == &rhs)
{
return *this;
}
denom = rhs.denom;
nominator = rhs.nominator;
return *this;
}
};
void debug(const Fraction& obj)
{
cout << "this is debug: pass by reference " <<endl;
}
void debugPassByValue(Fraction obj)
{
cout << "this is debug: pass by value" <<endl;
}
int main()
{
Fraction A(1,2);
cout << "--------------" <<endl;
debug(A);
cout << "--------------" <<endl;
A = Fraction(2,3);
cout << "--------------" <<endl;
debugPassByValue(A);
cout << "--------------" <<endl;
cin.get();
return 0;
}
You will see the following output:
call non-copy constructor //Fraction A(1,2);
--------------
this is debug: pass by reference //debug(A);
--------------
call non-copy constructor //A = Fraction(2,3);---> construct temporary object
call copy assignment operator //A = Fraction(2,3);
--------------
call copy constructor //debugPassByValue(A);
this is debug: pass by value
--------------
Now you will have a clearer view of what are called.
In the following we will consider [x] to mean that x is optional.
I am confused over the differences between passing objects by reference and by value to functions of a particular class.
When you pass an object by value, the program must create the local object of the function therefore it calls the copy constructor of the class to create this object. When you pass by reference (and seemingly by pointer) the variable object1 inside the function is just an alias of the object you passed to the function; therefore if you edit the one inside the function, the edits will be applied to the outside object as well.
Does the aforementioned line call the default constructor to make a temporary object Fraction(2,3) and then the assignment operator?
It's the assignment operator. Considering A to be an already declared variable of type X it will be called X Fraction::operator=([const] Fraction[&]) or any compatible type.
Notice: when declaring Fraction x = Fraction(2, 3) it won't be used the operator= as you may expect, the corresponding constructor will be called instead (in this case Fraction::Fraction([const] Fraction[&])).
Indeed in the case of debug no copy is made.
In the second case, I'm not quite sure I understand your question. This line:
A = Fraction(2,3);
Should use Fraction's assignment operator. A already exists, so it uses the assignment operator on instance A to assign it Fraction(2,3) which is a temporary object.
I'm having difficulty wrapping my mind around the following (specifically, scenario b):
(Assume I have defined an assignment operator, addition operator, and copy constructor just to output the fact that they are being called)
scenario a:
Simple a;
Simple b;
Simple c = a + b;
The output is as follows:
Simple constructor called
Simple constructor called
Simple add operator call
Simple constructor called
copy constructor called
-- This is all fine and dandy
scenario b (the behavior that I cannot understand):
Simple d;
Simple e;
Simple f;
f = d + e;
Simple constructor called
Simple constructor called
Simple constructor called
Simple add operator called
Simple constructor called
copy constructor called
assignment operator called
The question that I have is that in scenario b, why is the copy constructor called right before the assignment operator is? To my understanding, a copy constructor will only be called on an uninitialized object. However, in this scenario, the object f has been initialized in the line preceding the addition.
An explanation would be greatly appreciated.
Apologies for not posting the source code right away (and for the lack of indentation - I am having problems copying to the textarea). Here it is in all of it's simplicity.
I am using Visual Studio 2005. Unfortunately, I am not that familiar with the workings of it yet, hence I cannot specify the optimization parameters that are being passed to the compiler.
class Simple
{
public:
Simple(void);
Simple operator +(const Simple& z_Simple) const;
Simple& operator =(const Simple& z_Simple);
Simple(const Simple& z_Copy);
int m_Width;
int m_Height;
public:
~Simple(void);
};
#include "Simple.h"
#include <iostream>
using std::cout;
using std::endl;
Simple::Simple(void)
{
this->m_Height = 0;
this->m_Width = 0;
cout << "Simple constructor called" << endl;
}
Simple::Simple(const Simple& z_Copy)
{
cout << "copy constructor called" << endl;
this->m_Height = z_Copy.m_Height;
this->m_Width = z_Copy.m_Width;
}
Simple& Simple::operator =(const Simple &z_Simple)
{
cout << "assignment operator called" << endl;
this->m_Height = z_Simple.m_Height;
this->m_Width = z_Simple.m_Width;
return *this;
}
Simple Simple::operator +(const Simple &z_Simple) const
{
cout << "Simple add operator called" << endl;
int y_Height = this->m_Height + z_Simple.m_Height;
int y_Width = this->m_Width + z_Simple.m_Width;
Simple y_Ret;
y_Ret.m_Height = y_Height;
y_Ret.m_Width = y_Width;
return y_Ret;
}
Simple::~Simple(void)
{
cout << "destructor called" << endl;
}
Certainly Nemo's explanation is the one that my novice C++ mind can grasp :)
After changing the optimization level to /O2, I can see the output of scenario b as follows (and what I would have expected)
Simple constructor called
Simple constructor called
Simple constructor called
Simple add operator called
Simple constructor called
assignment operator called
Thank you all for your suggestions.
Your + operator returns a object by value, which might result in call to copy constructor if the compiler did not elide it.
Simple Simple::operator +(const Simple &z_Simple) const
{
//......
Simple y_Ret;
//......
return y_Ret;
}
Code:
Simple d;
Simple e;
Simple f;
f = d + e;
Here is a step by step analysis:
Simple constructor called ---> creation of `d`
Simple constructor called ---> creation of `e`
Simple constructor called ---> creation of `f`
Simple add operator called ---> Inside Addition operator
Simple constructor called ---> creation of local `y_Ret`
copy constructor called ---> `y_Ret` returned by value
assignment operator called ---> Result returned by `+` used for `=`
I made the following operator overloading test:
#include <iostream>
#include <string>
using namespace std;
class TestClass
{
string ClassName;
public:
TestClass(string Name)
{
ClassName = Name;
cout << ClassName << " constructed." << endl;
}
~TestClass()
{
cout << ClassName << " destructed." << endl;
}
void operator=(TestClass Other)
{
cout << ClassName << " in operator=" << endl;
cout << "The address of the other class is " << &Other << "." << endl;
}
};
int main()
{
TestClass FirstInstance("FirstInstance");
TestClass SecondInstance("SecondInstance");
FirstInstance = SecondInstance;
SecondInstance = FirstInstance;
return 0;
}
The assignment operator behaves as-expected, outputting the address of the other instance.
Now, how would I actually assign something from the other instance? For example, something like this:
void operator=(TestClass Other)
{
ClassName = Other.ClassName;
}
The code you've shown would do it. No one would consider it to be a particularly good implementation, though.
This conforms to what is expected of an assignment operator:
TestClass& operator=(TestClass other)
{
using std::swap;
swap(ClassName, other.ClassName);
// repeat for other member variables;
return *this;
}
BTW, you talk about "other class", but you have only one class, and multiple instances of that class.
The traditional canonical form of the assignment operator looks like this:
TestClass& operator=(const TestClass& Other);
(you don't want to invoke the copy constructor for assignment, too) and it returns a reference to *this.
A naive implementation would assign each data member individually:
TestClass& operator=(const TestClass& Other)
{
ClassName = Other.ClassName;
return *this;
}
(Note that this is exactly what the compiler-generated assignment operator would do, so it's pretty useless to overload it. I take it that this is for exercising, though.)
A better approach would be to employ the Copy-And-Swap idiom. (If you find GMan's answer too overwhelming, try mine, which is less exhaustive. :)) Note that C&S employs the copy constructor and destructor to do assignment and therefore requires the object to be passed per copy, as you had in your question:
TestClass& operator=(TestClass Other)
almost all said, a few notes:
check for self-assignment, i.e. if (&other != this) // assign
look here for an excellent guide on operator overloading
Traditionnaly the assignment operator and the copy constructor are defined passing a const reference, and not with a copy by value mechanism.
class TestClass
{
public:
//...
TestClass& operator=(const TestClass& Other)
{
m_ClassName= Other.m_ClassName;
return *this;
}
private:
std::string m_ClassName;
}
EDIT: I corrected because I had put code that didnt return the TestClass& (c.f. #sbi 's answer)
You are correct about how to copy the contents from the other class. Simple objects can just be assigned using operator=.
However, be wary of cases where TestClass contains pointer members -- if you just assign the pointer using operator=, then both objects will have pointers pointing to the same memory, which may not be what you want. You may instead need to make sure you allocate some new memory and copy the pointed-to data into it so both objects have their own copy of the data. Remember you also need to properly deallocate the memory already pointed to by the assigned-to object before allocating a new block for the copied data.
By the way, you should probably declare your operator= like this:
TestClass & operator=(const TestClass & Other)
{
ClassName = Other.ClassName;
return *this;
}
This is the general convention used when overloading operator=. The return statement allows chaining of assignments (like a = b = c) and passing the parameter by const reference avoids copying Other on its way into the function call.