Default Constructor, Copy Constructor and Destructor are very important and I understand why C++ implicitly defined them. Just think about function arguments that should be copied, local variables that should be destructed and objects that should be construct-able even if you don't say how to construct.
But why do we need the copy assignment operator implicitly defined? Is it really a must to be able to do a = b? It is not game changing, right? Any strong reason I don't know?
... why do we need the copy assignment operator?
Simply, to support assignment semantics. These are not the same as copy construction semantics.
Foo f1;
Foo f2(f1); // copy...
Foo f3;
f3 = f1; // assignment...
They are similar, and often implemented in terms of one another, but not the same.
Why would they all be implicitly defined?
To support and mimic the C-style value semantics. So that user defined types can support the same semantics as the built in types.
Side note; IIRC, there has been some deprecation of rules here with the onset of move semantics...
I believe it comes from one of the properties/capabilities of C++ which is being able to compile the native C code.
In C you are able to assign one struct variable to another e.g.
typedef struct foo_s
{
int field1;
int field2;
} foo_t;
int main ()
{
foo_t a, b;
a.field1 = 1;
a.field2 = 2;
b = a;
return 0;
}
So you should be able to compile this code as C++ thus you must have a default assignment operator.
If you copy-construct an object, you are able to set const members. Imagine you already have an object and then want to do an assignment. This would violate the constness of the members.
To clarify this a bit - because this short text seemed to be not sufficient for the most people - here some code:
struct C {
const int i;
C(int i) :i(i) {};
};
int main(void) {
C a(5), b(7);
a = b; // compiler error
C c(a); // no error
return 0;
}
So you need the assignment operator, because it has different semantics.
It is implicitly defined - if possible - because assignment should be defined by default whenever it is possible. Note, that in most cases your assignment operator is actually not implicitly defined. The above code is just one example, but there are many other cases: if you have a user declared move constructor for instance, then the copy assignment operator is not implicitly defined.
Related
(I just realized I first need to solve a much more basic issue with copying unions: When a union object is copied, is a member subobject created?. Please see that other question first.)
The implicitly generated copy operations (constructor and assignment) of a class perform member by member copy (initialization or assignment). (For a trivial type these are the same.)
So a class with some members not initialized cannot be copied, as accessing uninitialized objects is illegal.
struct C {
int m1, m2;
};
void f() {
C c1, c2;
c1.m1 = 1;
c2 = c1; // not initialized
}
But a union can always be copied, even if it contains class members, some of which aren't initialized (because... by definition not two members of a unions are initialized).
Does that mean that copying a union of a class with uninitialized members is legal:
union U {
C m;
};
void g() {
U u1, u2;
u1.m.m1 = 1;
u2 = u1;
}
and if so, can classes be copied by casting to such union?
void f2() {
C c1, c2;
c1.m1 = 1;
(U&)c2 = (U&)c1; // not initialized?
}
Yes, you can copy the union with the uninitialized (indirect) member—via the default copy/move assignment operator that is defined to copy the object representation. (You could also write your own operator that used std::memcpy. It is obviously a wording defect if the defaulted operator doesn’t establish the correct active member, and is std::memcpy should do so as well.)
However, you cannot use cast-to-union as a safer copy for normal class objects; the call to the assignment operator has undefined behavior per a special rule needed because it doesn’t involve an access to any (scalar) object. Your own std::memcpy-based implementation would be fine, even via reinterpret_cast(!), but that’s not newsworthy—reading/copying indeterminate values via unsigned char (and perhaps char) and std::byte is always allowed).
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
};
Let us consider the following classes
class test1
{
private:
int a;
int b;
public:
test1():a(0),b(0){}
};
class test2
{
private:
int a;
int b;
public:
test2()
{
a=0;
b=0;
}
};
Now, I know that test1() constructor is the right way to initialize the data members of a class, because in test2() we are performing assignment and not initialization. My questions are:
What might go wrong if we perform assignment instead of initialization?
Doesn't the compiler internally perform assignment in case of test1() constructor? If not, then how are these initialized?
What might go wrong if we perform assignment instead of initialization?
Some class types (and also references and const objects) can't be assigned; some can't be default-initialised; some might be more expensive to default-initialise and reassign than to initialise directly.
Doesn't the compiler internally performs assignment in case of test1() constructor? If no then how are these initialized?
In the case of primitive types like int, there is little or no practical difference between the two. Default-initialisation does nothing, and direct-initialisation and assignment both do essentially the same thing.
In the case of class types, default-initialisation, assignment and direct-initialisation each call different user-defined functions, and some operations may not exist at all; so in general the two examples could have very different behaviour.
For your example there is no real different because you are initializing plain integers.
But assume these integers are objects with constructors, then the compiler would generate the following calls:
// test1
a::copy_constructor(0);
b::copy_constructor(0);
// test2
a::default_constructor();
b::default_constructor();
a::operator = (0);
b::operator = (0);
So depending on your objects test2 could have a huge performance impact. Also by initializing your objects in the initializing lists guaranties that your variables have the data when you enter the constructor. One 'drawback' of the initializer list is that the it is executed in the order that the variables are declared and not in the order of the initializer list, so it could be that you don't want to use the initializer list.
A third variant utilizing direct-list-initialization. Advantages are
variables don't have to be default-initialized in the constructor
the constructor and not the copy/move assignment operator is used to construct the members (as the other answers said, it doesn't matter for the primitive type of this example)
So this is less error-prone than both variants in the question:
#include <iostream>
class Test3
{
public: // Made public so we can easily output the vars. No struct is used to be consistent with question.
int a { 4 }; // No need for separate initialization in constructor.
int b { 2 }; // No need for separate initialization in constructor.
};
int main()
{
const auto t = std::move(Test3()); // Implicitly-declared default constructor + implicitly-declared move assignment operator
std::cout << t.a << t.b;
}
Output is 42.
Constructor-initializers allow initialization of data members at the time of their
creation.
Some programmers prefer to assign initial values in the body of the constructor. However, several data types must be initialized in a constructor-initializer. The following lines summarizes them:
const data members
You cannot legally assign a value to a const variable
after it is created. Any value must be supplied at the
time of creation.
Reference data members
References cannot exist without referring to
something.
Object data members for which there is no default constructor
C++ attempts to initialize member objects using a
default constructor. If no default constructor exists, it
cannot initialize the object.
I have a class which does not have copy constructor or operator= overloaded.
The code is pretty big but the issue is around this pseudo-code:
ClassA object1(x,y);
object1.add(z)
myVector.push_back(object1);
//Now when I retrieve from myVector and do add it
// apparently creates another object
myVector.at(index).add(z1);
Like I said it is pseudo-code. I hope it make sense to experts out there!
So, ClassA looks like this (of course not all data members included)
Class ClassA {
private:
int x;
string y;
ClassB b;
vector<int> i;
public:
int z;
}
Since ClassB b is a new data member for this release, is the need of copy constructor now become a must?
Thanks again all of you for responding.
Class ClassB {
private:
vector<ClassC*> c;
Class D
}
Class ClassC {
private:
vector<ClassE*> e;
}
Class ClassD{
private:
vector<ClassF*> f;
}
Then ClassE and ClassF have basic types like int and string.
The new object isn't being created when you retrieve the object using
at(); at() returns a reference to it. The new object is being
created when you do the push_back(). And if you don't have an
accessible copy constructor or assignment operator, you can't put the
object into a vector; officially, it's undefined behavior, but at least
if you use the vector (as you've done here), it will in fact not
compile. Most likely you're getting the compiler generated defaults.
Without seeing the actual object type, it's impossible for us to say
whether they're appropriate; if the object type only contains basic
types and types from the standard library (other than the iostream stuff
and auto_ptr—and the threading stuff if you're using C++11),
then the compiler generated copy constructor and assignment should be
OK. If the class contains pointers to memory you allocate in the
constructor, it almost certainly isn't.
It must create a temporary when retrieving because the value stored is a copy.
I have a class which does not have copy constructor or operator= overloaded.
This does not really matter. Unless you explicitly disable them (declare as private and don't implement in C++03, define as delete in C++11) the compiler will generate one for you.
Now, if you do disable copying of your objects you will realize that you can no longer store them directly in a vector, as the vector will copy the argument internally. You could on the other hand store a (smart) pointer in the container, but this might actually complicate the problem you want solved.
I doubt that it is using a copy constructor, but just plain old struct copy.
myVector.at(index)
returns ClassA in this case, not ClassA &
You should be able to solve this by:
ClassA & refObj = myVector.at(index);
refObj.add(z1);
I put together a little test which had surprising results on Visual Studio.
class Boo {
public:
void add() { a++; }
int a;
};
vector<Boo> v;
Boo b;
b.a = 1;
for (int i=0; i<5; i++) {
v.push_back(b);
}
v[0].add();
v.at(1).add();
Boo & refB = v[2]; refB.add();
refB = v.at(3); refB.add();
Boo & refB2 = v.at(4); refB2.add();
printf("%d %d %d %d %d\n", v[0].a, v[1].a, v[2].a, v[3].a, v[4].a);
Results:
2 2 2 1 2
So the compiler treats:
Boo & refB = v[2]; refB.add();
refB = v.at(3); refB.add();
Differently than:
Boo & refB2 = v.at(4); refB2.add();
But I don't get the original problem (that is, v.at(n).add() is not copying, but passing by reference). Are you sure the following line is copying?
myVector.at(index).add(z1)
Could this be compiler specific? What compiler / OS are you using?
I have a class which does not have copy constructor or operator= overloaded.
In that case, the compiler provides these for you. Quoting the C++ 2003 standard, clause 12.8:
If the class definition does not explicitly declare a copy constructor, one is declared implicitly. … The implicitly-declared copy constructor for a class X will have the form
X::X(const X&) [or] X::X(X&). … The implicitly-defined copy constructor for class X performs a memberwise copy of its subobjects.
and
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. … The implicitly-declared copy assignment operator for a class X will have the form X& X::operator=(const X&) [or] X& X::operator=(X&). … The implicitly-defined copy assignment operator for class X performs memberwise assignment of its subobjects.
The push_back creates a copy as it receives the parameter by value. If you dont want to use a copy constructor define the vector as a vector of pointers to your object, and push a pointer instead.
Consider the following:
class A {
public:
const int c; // must not be modified!
A(int _c)
: c(_c)
{
// Nothing here
}
A(const A& copy)
: c(copy.c)
{
// Nothing here
}
};
int main(int argc, char *argv[])
{
A foo(1337);
vector<A> vec;
vec.push_back(foo); // <-- compile error!
return 0;
}
Obviously, the copy constructor is not enough. What am I missing?
EDIT:
Ofc. I cannot change this->c in operator=() method, so I don't see how operator=() would be used (although required by std::vector).
I'm not sure why nobody said it, but the correct answer is to drop the const, or store A*'s in the vector (using the appropriate smart pointer).
You can give your class terrible semantics by having "copy" invoke UB or doing nothing (and therefore not being a copy), but why all this trouble dancing around UB and bad code? What do you get by making that const? (Hint: Nothing.) Your problem is conceptual: If a class has a const member, the class is const. Objects that are const, fundamentally, cannot be assigned.
Just make it a non-const private, and expose its value immutably. To users, this is equivalent, const-wise. It allows the implicitly generated functions to work just fine.
An STL container element must be copy-constructible and assignable1(which your class A isn't). You need to overload operator =.
1
: §23.1 says The type of objects stored in these components must meet the requirements of CopyConstructible
types (20.1.3), and the additional requirements of Assignabletypes
EDIT :
Disclaimer: I am not sure whether the following piece of code is 100% safe. If it invokes UB or something please let me know.
A& operator=(const A& assign)
{
*const_cast<int*> (&c)= assign.c;
return *this;
}
EDIT 2
I think the above code snippet invokes Undefined Behaviour because trying to cast away the const-ness of a const qualified variable invokes UB.
You're missing an assignment operator (or copy assignment operator), one of the big three.
The stored type must meet the CopyConstructible and Assignable requirements, which means that operator= is needed too.
Probably the assignment operator. The compiler normally generates a default one for you, but that feature is disabled since your class has non-trivial copy semantics.
I think the STL implementation of vector functions you are using require an assignment operator (refer Prasoon's quote from the Standard). However as per the quote below, since the assignment operator in your code is implicitly defined (since it is not defined explicitly), your program is ill-formed due to the fact that your class also has a const non static data member.
C++03
$12.8/12 - "An implicitly-declared
copy assignment operator is implicitly
defined when an object of its class
type is assigned a value of its class
type or a value of a class type
derived from its class type. A program
is illformed if the class for which a
copy assignment operator is implicitly
defined has:
— a nonstatic data member of const type, or
— a nonstatic data
member of reference type, or
— a
nonstatic data member of class type
(or array thereof) with an
inaccessible copy assignment operator,
or
— a base class with an inaccessible
copy assignment operator.
Workaround without const_cast.
A& operator=(const A& right)
{
if (this == &right) return *this;
this->~A();
new (this) A(right);
return *this;
}
I recently ran into the same situation and I used a std::set instead, because its mechanism for adding an element (insert) does not require the = operator (uses the < operator), unlike vector's mechanism (push_back).
If performance is a problem you may try unordered_set or something else similar.
You also need to implement a copy constructor, which will look like this:
class A {
public:
const int c; // must not be modified!
A(int _c)
...
A(const A& copy)
...
A& operator=(const A& rhs)
{
int * p_writable_c = const_cast<int *>(&c);
*p_writable_c = rhs.c;
return *this;
}
};
The special const_cast template takes a pointer type and casts it back to a writeable form, for occasions such as this.
It should be noted that const_cast is not always safe to use, see here.
I just want to point out that as of C++11 and later, the original code in the question compiles just fine! No errors at all. However, vec.emplace_back() would be a better call, as it uses "placement new" internally and is therefore more efficient, copy-constructing the object right into the memory at the end of the vector rather than having an additional, intermediate copy.
cppreference states (emphasis added):
std::vector<T,Allocator>::emplace_back
Appends a new element to the end of the container. The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container.
Here's a quick demo showing that both vec.push_back() and vec.emplace_back() work just fine now.
Run it here: https://onlinegdb.com/BkFkja6ED.
#include <cstdio>
#include <vector>
class A {
public:
const int c; // must not be modified!
A(int _c)
: c(_c)
{
// Nothing here
}
// Copy constructor
A(const A& copy)
: c(copy.c)
{
// Nothing here
}
};
int main(int argc, char *argv[])
{
A foo(1337);
A foo2(999);
std::vector<A> vec;
vec.push_back(foo); // works!
vec.emplace_back(foo2); // also works!
for (size_t i = 0; i < vec.size(); i++)
{
printf("vec[%lu].c = %i\n", i, vec[i].c);
}
return 0;
}
Output:
vec[0].c = 1337
vec[1].c = 999