Class Assignment Operators - c++

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.

Related

In C++, what is the proper way to do copy/move/assignment for objects with a unique id?

/* Example.h file */
class Example {
public:
Example(const std::string& unique_id_, int attribute1_, int attribute2_,):
unique_id(unique_id_), attribute1(attribute1_), attribute2(attribute2_){};
void set_attribute1(int attribute1_){ attribute1 = attribute1_; }
void set_attribute2(int attribute2_){ attribute2 = attribute2_; }
/* Deleting copy/move/assignment operators to make each instance unique */
Exercise_Data(const Exercise_Data&) = delete;
Exercise_Data(Exercise_Data&&) = delete;
Exercise_Data& operator= (Exercise_Data&) = delete;
Exercise_Data& operator= (Exercise_Data&&) = delete;
private:
const std::string unique_id;
int attribute1;
int attribute2;
}
Currently, this is my code. In multiple places in the calling code, I want to be able to edit all of the attributes of this class with the exception of the unique_id. If I add another attribute, then the calling code (in multiple places) would have to be edited to be able to set that new attribute.
Is there a good way to allow for all attributes with the exception of the unique id to be edited?
Is it ok to overload the assignment and move operators to do this and still delete the copy constructor?
Anyone have a good example of a class with unique ids?
Is there a good way to allow for all attributes with the exception of the unique id to be edited?
Just make sure that the id is private and you never return a non-const pointer / reference to it. Making sure that the member functions don't modify the id should be trivial.
Is it ok to overload the assignment and move operators to do this and still delete the copy constructor?
It is OK for class to be movable but not copyable. Such types are called move-only.
You won't be able to implement the move operations if you want to keep the member const. I recommend against const members.
Anyone have a good example of a class with unique ids?
std::unique_ptr is an example of such class essentially. The "id" represents some unique resource that is cleaned by the destructor.
If each object has a unique, permanent identity, then you probably can't really support copying--a copy of an object should be the same as the original object, and in this case you're saying each should be unique, so having a copy probably doesn't make sense.
Move construction, on the other hand, should be more reasonable. Although an ID moves from one object to another, there's still only one object with a given identity at a given time.
The move ctor is pretty simple and straightforward. Just initialize your members in the member initializer list like usual, including the fact that since it's a move ctor, you want to move from the unique_id in the source:
Example(Example&& other)
: unique_id(std::move(other.unique_id))
, attribute1(other.attribute1)
, attribute2(other.attribute2)
{
}
In theory, you probably should use std::move on attribute1 and attribute2 as well, but since they're ints, it won't normally make any real difference.
Then we get to the one that's a little bit non-trivial: move assignment. Since we've defined unique_id as const, we can't just copy over it. To do the job, we first destroy the current contents of the destination object, then use placement new to do a move construction from the source to the destination:
Example &operator=(Example&& other) {
this->~Example();
new (this) Example(std::move(other));
return *this;
}
This works because const qualification has no effect during destruction or construction, so that's basically the only way we can implement our assignment.
Here's a sample demonstrating it all in action:
#include <string>
#include <new>
#include <iostream>
class Example {
public:
Example(const std::string& unique_id_, int attribute1_, int attribute2_):
unique_id(unique_id_), attribute1(attribute1_), attribute2(attribute2_){};
void set_attribute1(int attribute1_){ attribute1 = attribute1_; }
void set_attribute2(int attribute2_){ attribute2 = attribute2_; }
/* Deleting copy/move/assignment operators to make each instance unique */
Example(const Example&) = delete;
Example& operator= (Example&) = delete;
Example(Example&& other)
: unique_id(std::move(other.unique_id))
, attribute1(other.attribute1)
, attribute2(other.attribute2)
{
}
Example &operator=(Example&& other) {
this->~Example();
new (this) Example(std::move(other));
return *this;
}
private:
const std::string unique_id;
int attribute1;
int attribute2;
friend std::ostream &operator<<(std::ostream &os, Example const &e) {
return os << "ID: " << e.unique_id;
}
};
int main() {
Example a("A", 1, 2);
std::cout << "A: " << a << "\n";
Example b{std::move(a)}; // move construction
std::cout << "B: " << b << "\n";
Example c("B", 3, 4); // construct a destination object
c = std::move(b); // move assign into it
std::cout << "C: " << c << "\n";
}
As to real-life examples of unique ids in objects...without going back on searching through code, I'm not sure. I can remember a few that seem like they're probably at least pretty close (and certainly others that have const members, that use the same destroy/placement new "trick" to do assignment), but I'm not quite ambitious enough to look through them to check whether there are any that are quite identical to this or not.

overloading copy assignment operator with classes without a reference parameter name

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;

calling copy constructor in assignment operator

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.

C++, Proper copy operator overloading

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.

Why there is a copy before assign?

I am doing the following test:
#include <iostream>
#include <vector>
using namespace std;
class A
{
private:
int i;
public:
A():i(1){cout<<"A constr"<<endl;}
A(const A & a):i(a.i){cout<<"A copy"<<endl;}
virtual ~A(){cout<<"destruct A"<<endl;}
void operator=(const A a){cout<<"A assign"<<endl;}
};
int main()
{
A o1;
A o2;
o2=o1;
}
And the output is:
A constr
A constr
A copy
A assign
destruct A
destruct A
destruct A
It seems that "o2=o1" did a copy first followed by an assignment, and I wonder what's the story behind it. Thanks!
Because you pass by value into your assignment operator:
void operator=(const A a)
You probably meant to pass by reference and you should also return a reference to the assigned-to object:
A& operator=(const A& a) { std::cout << "A assign" << std::endl; return *this; }
You seem to set up you assignment operator to be implemented properly:
T& T::operator= (T value) {
value. swap(*this);
return *this;
}
The argument is passed by copy to the assigment operator and the compiler actually needed to do this copy in your set up. If you had passed a temporary the copy could have been avoided:
o2 = A();
Thus, the implementation above actually has a few interesting properties:
it leverages already written functions: the copy constructor is either generated or written but does the Right Thing and if you want to have an assignment you probably want to have swap() member as well
the assignment is strong exception safe if the swap() operation is non-throwing as it should be. When allocators enter the picture things need to be done slightly different, though
the assignment tries to avoid actual copy operations as the copy during argument passing can be elided in some cases, i.e. the content is just swap()ed into place