I've built a quaternion class and want to know if I should overload the = operator. Is there any greater benefit to overloading this? What are the detriments to not overloading it?
If I do overload it it will be done like this:
if (this != &Input)
{
w = Input.w;
x = Input.x;
y = Input.y;
z = Input.z;
Normalise();
CalculateMatrix();
}
return *this;
I've built a quaternion class and want to know if I should overload
the = operator. Is there any greater benefit to overloading this? What
are the detriments to not overloading it?
You haven't shown your class definition. Without this one would not be able to comment. Typically, if your class does (not) have ownership of any indirect members (pointers), then the default generated copy constructor / assignment operator is adequate. These defaults performs memberwise copy (copies every member) by value. If one of your members happen to be a pointer, this would be copied by value too (and the rhs/source of the assignment would still contain the original pointer). The problem arises during first deletion of the either source/destination of assignment, when one of the objects delete the "owned" memory, whilst the other is still holding onto it (via indirect member).
Related
Door::Door(const Door& door)
{
*this = door;
}
I am very confused with this copy constructor. For me, it does not make sense to use *this=door, but rather
Door::Door(const Door& door)
{
this->color = door.color;
this->destination = door.destination;
}
Can you explain why *this=door is correct?
EDIT
As it is suggested, I need to overload = for the class. So I got something like
const Porte& operator =(const Porte& source)
{
this->color = door.color;
this->destination = door.destination;
}
So I am right to use *this=door now. Why isn't it rather this=door instead?
It invokes Door::operator=(Door const &other).
If that operator is explicitly implemented, it does whatever the implementation says it does. If it's implicitly generated, it simply copies the values of all members.
this is a prvalue pointer. Pointers do not have members and correspondingly you cannot apply the member access operator on a pointer.
this.color = door.color;
this.destination = door.destination;
To access the pointed object through a pointer, you must indirect through the pointer using an indirection operator. In the first program, the unary * is used which is the indirection operator.
There is also a member access indirection operator -> which is a shorthand for indirecting through a pointer and accessing member of the pointed object. As such, you could write:
this->color = door.color;
this->destination = door.destination;
The first example instead uses the assignment operator to do the same thing.
Neither of these is something that I would recommend. Best way to implement a copy constructor is in order of preference. If the first one is not applicable, then use the next and so on:
If you don't need to, don't declare copy constructor at all and use the implicitly generated one.
Define the constructor as explicitly defaulted (or deleted).
Initialise the members and leave the constructor body empty.
why it is not this=door instead?
Because it is not supposed to assign the pointer. It is supposed to assign the pointed object. As I explained above, you must indirect through the pointer (using indirection operator) in order to access the pointed object.
Besides, this is an rvalue, so it cannot be assigned.
this was a recent T/F question that came up in my cs course that I found a bit confusing.
The textbook states:
The = operator may be used to assign one object's data to another object, or to initialize one object with another object's data. By default, each member of one object is copied to its counterpart in the other object.
The question verbatim was:
You cannot use the = operator to assign one object's values to another object, unless you overload the operator. T/F?
Judging from that particular paragraph of the textbook, I answered false. However, it turns out the quiz answer was actually true.
When I looked up the question online, I saw other sources listing the answer as "false" as well. Granted, these were just generic flashcard / quiz websites, so I don't put much stock in them.
Basically, I'm just curious what the real answer is for future studying purposes.
P.S.: The textbook later goes on to state: "In order to change the way the assignment operator works, it must be overloaded. Operator overloading permits you to redefine an existing operator’s behavior when used with a class
object."
I feel like this is related and supports the "true" answer, but I'm not really sure.
The statement
” You cannot use the = operator to assign one object's values to another object, unless you overload the operator.
… is trivially false, because you can assign to e.g. an int variable. Which is an object. In C++.
Possibly they intended to write “class-type object”, but even so it's false. E.g. any POD (Plain Old Data) class type object is assignable and lacks an overloaded assignment operator.
However, there are cases where you want or need to prohibit or take charge of assignment.
If you don't implement the assignment operator yourself, the compiler will generate one for you, which will copy the data from the source to the destination, invoking assignment operators of the members of your class where possible/necessary.
This does not work if your class e.g. features const members, and it does not yield the desired result if your class contains pointers to dynamically allocated objects.
So, I would also say that the statement is false.
That's a trick question. The syntactic answer is "false" but the semantic answer is "maybe".
struct Foo
{
int a;
double b;
};
Foo foo1;
Foo foo2;
foo2 = foo1; // Ok in both senses.
struct Bar
{
Bar() : arr(new int[20]) {}
~Bar() { delete [] arr; }
int* arr;
};
Bar bar1;
Bar bar2;
bar2 = bar1; // Ok in syntax not in semantics
// This will lead to UB
In C++, when User-Defined Types such as class and struct are involved then its better to overload operator= for the Type. Using default assignment operator with object of class or struct will involve Shallow Copy. Shallow Copy sometimes lead to undefined behavior when the class or struct objects have dynamically allocated memory inside them.
Appropriately overloading operator= for class and struct types will lead to Deep Copy which is the correct method of assigning an object ObjA to object ObjB (ObjB = ObjA) when ObjA and ObjB are objects of some class or struct and contain dynamically allocated memory inside them.
The = operator may be used to assign one object's data to another object, or to initialize one object with another object's data. By default, each member of one object is copied to its counterpart in the other object. This is true when object of class/struct has only static fundamental type of data inside it. Here,
by static it means that all the memory requirement of object is fixed at compile time only.
You cannot use the default = operator to assign one object's values to another object, unless you overload the operator. This is true when object of class/struct has some dynamically allocated memory inside it, possibly by using pointers.
For more reference see:
What is the difference between a deep copy and a shallow copy?
You cannot use the = operator to assign one object's values to another object, unless you overload the operator. T/F?
Ans: F
You can assign one object's values to another object without overloading = operator, as compiler by default defines assignment operator for the class in all case except for below
you have explicitly declared a copy-assignment operator (for class X an operator = taking X, X& or const X&) )
there is a member in your class that is not assignable (such as a reference, a const object or a class with no or inaccessible assignment operator)
But you need to understand why it becomes mandatory to overload the assignment operator in case you are having user defined members or having dynamic memory allocation,for this read concept of shallow and deep copy
Unit now I've never needed to overload the assignment parameter or write a Copy Constructor
(at least, it seems I never had to, because I never had problems)
as far as I know the Assignment Operator must be overloaded when I want to copy a Object and its values to another Object after initialization (Same of CopyConstructor, but when its initialized).
Object a;
Object b;
// do something with a
b = a; //for this line, the Assignment Parameter must be overloaded (nearly never do such things)
Object c = a; // needs a Copy constructor
But If I would write it like this:
Object a;
Object* b;
b = &a; //I think I won't need one here since b actually points to Object a
Object* c = a; // I think same here
But what should I do, if I copy a object outside of its own class/parent class.
class MyOtherClass{Object obj..........}
void MyOtherClass::SetObject(Object obj)
{
this->obj = obj; // Assignment operator overloading needed in class Object?
}
do I have to overload the Assignment Operator in the Object class?
What if I do following:
void MyOtherClass::SetObject(Object &obj)
{
this->obj = obj; // Assignment operator overloading needed in class Object?
}
And the final question, does this also include Enums?(I think yes)
void MyOtherClass::SetObject(ENumClass Eobj)
.
.
For the question of whether or not to implement an assignment operator, the answer is - it depends.
Remember that the compiler will generate its own default assignment operator if you do not provide one yourself (and in C++11, if you do not explicitly disable the default operator). The default operator performs an assignment of each individual class member using their own assignment operators.
So as long as your class members are assignment-safe in their own right, then the default operator is sufficient and you do not need to provide your own operator. You only need to provide your own operator when you need to perform custom actions, like deep-copying dynamically allocated members, debug logging, input validation, etc.
Why do you use a separate method SetObject() which duplicates operator=() semantic?
So that correctly assign a base class to derived you can overload operator=() in the derived object this way
DerivedObj& DerivedObj::operator=(const BaseObj& rhs){
// call parent operator= in functional form
BaseObj::operator=(rhs);
return *this;
}
Of course you should implement
BaseObj& BaseObj::operator=(const BaseObj& rhs)
However if you need assign base class to derived I would not say it's a good design
It is legal to write an assignment operator that assigns from a base type and it is also legal to write an assignment operator that assigns from a concrete object of the same type. However, even though both are legal, it is generally better design to implement assignment from an object of the same type or from other concrete types for which such an assignment actually makes sense and not from a general type.
That being said, it does vary from application to application. To be a little more specific, consider:
Shape
/ | \
Triangle Quadrilateral Circle
... it probably would not make sense to have an assignment operator in Circle that could assign from any Shape. What would that method even do? These different implementations have nothing in common.
Now, alternatively, consider:
Point2D
/ \
CartesianCoord PolarCoord
... in an application like this, it might make sense to provide an assignment operator that accepts a general Point2D in order to facilitate conversions between the two types. In this case, all implementations represent the same information and can be converted non-destructively between representations. As a result, allowing a more general conversion would make sense in this context.
So, really, it's all about what makes sense conceptually. If you can assign to/from an object in a way that does not destroy information, it is sane; if your assignment is destructive, it's likely a poor design.
As for the parameters, one typically uses "const T&" (where "T" is the type from which you are assigning) or "T&&" in the case of destructive assignment. The return type of the assignment should be a reference to an object of the same type as the current object. In other words:
class CartesianCoord : public Point2D {
public:
// ...
CartesianCoord& operator=(const Point2D&);
};
Or
class CartesianCoord : public Point2D {
public:
// ...
CartesianCoord& operator=(const Point2D&);
CartesianCoord& operator=(CartesianCoord&&); // fast destructive assignment
};
Or
class CartesianCoord : public Point2D {
public:
// ...
CartesianCoord& operator=(const CartesianCoord&); // if conversion disallowed
CartesianCoord& operator=(CartesianCoord&&); // fast destructive assignment
};
I read in another question that when implementing a move constructor it is good practice to std::move each member in the initializer list because if the member happens to be another object then that objects move constructor will be called. Like so...
//Move constructor
Car::Car(Car && obj)
:
prBufferLength(std::move(obj.prBufferLength)),
prBuffer(std::move(obj.prBuffer))
{
obj.prBuffer = nullptr;
obj.prBufferLength = 0;
}
However in all the sample move assignment operators I've seen, there has been no mention of using std::move for the same reasons. If the member is an object then should std::move be used? Like so...
//Move assignment
Car Car::operator=(Car && obj)
{
delete[] prBuffer;
prBufferLength = std::move(obj.prBufferLength);
prBuffer = std::move(obj.prBuffer);
obj.prBuffer = nullptr;
obj.prBufferLength = 0;
return *this;
}
UPDATE:
I appreciate there is no need to use std::move in the example I have chosen (poorly) however I'm interested in if the members were objects.
After reading the linked question, I can see the advice in the second most-upvoted answer is to use std::move in the initializer list for the move constructor because no matter if it is a primitive type or not, it will do the right thing. I somewhat disagree with that and think you should only call std::move where appropriate, but this is were personal preferences come in.
Also, for your move assignment operator, the way you have it is fine although I think the unnecessary call to std::move should be removed personally. Another option is to use std::swap which will do the right thing for you.
Car Car::operator=(Car && obj)
{
std::swap(this->prBufferLength, obj.prBufferLength);
std::swap(this->prBuffer, obj.prBuffer);
return *this;
}
The difference between the above move assignment operator and your move assignment operator is that the deallocation of memory is delayed while your version deallocates the memory right away, this might be important in some situations.
It looks like prBuffer is a pointer and prBufferLength is some kind of integral type, so move isn't going to make any difference in this particular case as they are both fundamental types.
If prBuffer was a std::string for example, then you should use move to force the use of a move constructor or move assignment operator.
If your member objects would benefit from moving, you should certainly move them. Another strategy, which was demonstrated in the answer you linked to, is swapping. Swapping is like moving, but it's moving in both directions. If your class manages a resource, this has the effect of the object that was passed in (the rvalue) recieving the no longer wanted data. That data is then destroyed by that object's destructor. For example, your move assignment operator could be written like this:
Car Car::operator=(Car && obj)
{
// don't need this, it will be handled by obj's destructor
// delete[] prBuffer;
using std::swap;
swap(prBuffer, obj.prBuffer);
swap(prBufferLength, obj.prBufferLength);
return *this;
}
Also take a look at the Copy-and-Swap idiom. Which allows you to use the same assignment operator for both move and copy, but has the slight drawback that self-assignment results in an unnecessary copy.
Like a lot of things in C++ and life there isn't a definitive yes or no answer to the question.
That said, as a general rule if incoming member will be cleared / reset / emptied and assigning the incoming member to the destination member will result in an assignment operator being called, then you will want to use std::move so that the move assignment operator will be called instead of the copy assignment operator.
If an assignment operator will not be called (i.e. just a shallow copy will be done) then using std::move is not necessary.
I believe a (the?) better way to implement move assignment is to use the move constructor to create a new temporary object, and then swap it with the current object, just as with copy assignment.
It not only avoids code duplication but also prevents you from making mistakes accidentally by e.g. forgetting to move a member.
I read about this from "Effective c++" ,this is Col.10.
It say it's a good way to have assignment operators return a reference to *this.
I wrote a code snippet to test this idea. I overridden the assignment operator here.And tested it. Everything is fine.
But when I remove that operator overriding, everything is the same. That means, the chaining assignment still works well. So, what am I missing? Why is that? Need some explanation from you guys, THank you.
#include <iostream>
using namespace std;
class Widget{
public:
Widget& operator=(int rhs)
{
return *this;
}
int value;
};
int main()
{
Widget mywidget;
mywidget.value = 1;
Widget mywidget2;
mywidget2.value = 2;
Widget mywidget3 ;
mywidget3.value = 3;
mywidget = mywidget2 = mywidget3;
cout << mywidget.value<<endl;
cout << mywidget2.value<<endl;
cout << mywidget3.value<<endl;
}
If you remove completely the operator= method, a default operator= will be created by the compiler, which implements shallow copy1 and returns a reference to *this.
Incidentally, when you write
mywidget = mywidget2 = mywidget3;
you're actually calling this default operator=, since your overloaded operator is designed to work with ints on the right side.
The chained assignment will stop working, instead, if you return, for example, a value, a const reference (=>you'll get compilation errors) or a reference to something different from *this (counterintuitive stuff will start to happen).
Partially related: the copy and swap idiom, i.e. the perfect way to write an assignment operator. Strongly advised read if you need to write an operator=
The default operator= will perform as if there were an assignment between each member of the left hand operand and each member of the right hand one. This means that for primitive types it will be a "brutal" bitwise copy, which in 90% of cases isn't ok for pointers to owned resources.
The question touches two different concepts, whether you should define operator= and whether in doing so you should return a reference to the object.
You should consider the rule of the three: if you define one of copy constructor, assignment operator or destructor you should define the three of them. The rationale around that rule is that if you need to provide a destructor it means that you are managing a resource, and in doing so, chances are that the default copy constructor and assignment operator won't cut it. As an example, if you hold memory through a raw pointer, then you need to release the memory in the destructor. If you don't provide the copy constructor and assignment operator, then the pointer will be copied and two different objects will try to release the memory held by the pointer.
While a pointer is the most common example, this applies to any resource. The exception is classes where you disable copy construction and assignment --but then again you are somehow defining them to be disabled.
On the second part of the question, or whether you should return a reference to the object, you should. The reason, as with all other operator overloads is that it is usually a good advice to mimic what the existing operators for basic types do. This is sometimes given by a quote: when overloading operators, do as ints do.
Widget& operator=(int rhs)
This allows you to assign an int to a Widget - e.g. mywidget = 3;
Make a Widget& operator=(Widget const & rhs) - it'll be called for your mywidget = mywidget2 = mywidget3; line.
You do not need an operator=(Widget const & rhs) though - the default should do fine.
Also, it may be a good idea to add e.g. cout << "operator=(int rhs)\n"; to your custom operator - then you'd see that it didn't get called at all in your code.