I'm taking a class on object oriented programming using C++.
In our text it says,
If we do not declare a copy constructor, the compiler inserts code
that implements a shallow copy. If we do not declare an assignment
operator, the compiler inserts code that implements a shallow
assignment.
What I want to know, is whether this is in fact true, what the alluded to compiler mechanism is actually called, and how it works.
This is not a question about copy constructors, it is about compiler behavior.
EDIT> More context
Copy Constructor as defined by the text:
The definition of a copy constructor contains logic that
performs a shallow copy on all of the non-resource instance variables
allocates memory for each new resource
copies data from the source resource(s) to the newly created resource(s)
Resource as defined by the text
Memory that an object allocates at run-time represents a resource of that
object's class.
The management of this resource requires additional logic that was unnecessary for simpler classes that do not access resources. This additional logic
ensures proper handling of the resource and is often called deep copying and
assignment.
It's more accurate to say that the compiler defines a default copy constructor and a default copy assignment operator. These will copy/construct the new object by simply calling the copy constructor for all of the member variables.
For primitives like ints and floats, this usually isn't a problem.
For pointers, though. This is bad news! What happens when the first object deletes that pointer? Now the other object's pointer is invalid!
If a member variable cannot be copied (perhaps you used a std::unique_ptr to fix the above problem), then the default copy assignment/ctor won't work. How can you copy something that can't be copied? This will lead to a compiler error.
If you define your own copy constructor/assignment operator, you can make a "deep copy" instead. You can:
Create a new object, rather than copying the pointer
Explicitly "shallow copy" a pointer
Mix the above two based on what you actually want!
Initialize member variables with default/custom values in copied objects rather than copy whatever was in the original object.
Disable copying altogether
On and on and on
As you can see, there are plenty of reasons why you'd want to implement (or explicitly prohibit) your own copy assignment operator, copy constructor, their move counterparts, and destructor. In fact, there's a well-known C++ idiom known as The Rule of Five (formerly the Rule of 3) that can guide your decision on when to do this.
Yes it's true, and it's indeed called shallow copying. As for how it works, lets say you have a pointer variable, and you assign it to another pointer variable. This only copies the pointer and not what it points to, this is a shallow copy. A deep copy would have created a new pointer, and copied the actual contents that the first pointer points to.
Something like this:
int* a = new int[10];
// Shallow copying
int* b = a; // Only copies the pointer a, not what it points to
// Deep copying
int* c = new int[10];
std::copy(a, a + 10, c); // Copies the contents pointed to by a
The problem with shallow copying in regards to pointers should be quite obvious: After the initialization of b in the above example, you have two pointers both pointing to the same memory. If one then does delete[] a; then both pointers become invalid. If the two pointers are in different objects of some class, then there is no real connection between the pointers, and the second object won't know if the first object have deleted its memory.
The code for shallow copy is a simple assignment for every field. If:
class S {
T f;
};
S s1, s2;
A assignment like s1=s2; is equivalent to what happens in the following:
class S {
T f;
public:
S &operator=(const S&s) {
this->f = s.f; // and such for every field, whatever T is
}
};
S s1, s2;
s1=s2;
This is stated in 12.8-8 of the draft standard:
The implicitly-declared copy constructor for a class X will have the
form X::X(const X&) if
— each direct or virtual base class B of X has
a copy constructor whose first parameter is of type const B& or const
volatile B&, and
— for all the non-static data members of X that are
of a class type M (or array thereof), each such class type has a copy
constructor whose first parameter is of type const M& or const
volatile M&.123
Otherwise, the implicitly-declared copy constructor
will have the form X::X(X&)
12.8-28 says:
The implicitly-defined copy/move assignment operator for a non-union
class X performs memberwise copy/move assignment of its subobjects. [...] in the order in which they were declared in the class definition.
I'll use a basic class to define the behavior of the compiler as best as I know how to.
class Student sealed {
private:
std::string m_strFirstName;
std::string m_strLastName;
std::vector<unsigned short> m_vClassNumbers;
std::vector<std::string> m_vTeachers;
std::vector<unsigned short> m_vClassGrades;
public:
Student( const std::string& strFirstName, const std::string& strLastName );
std::string getFirstName() const;
std::string getLastName() const;
void setClassRoster( std::vector<unsigned short>& vClassNumbers );
std::vector<unsigned short>& getClassRoster() const;
void setClassTeachers( std::vector<std::string>& vTeachers );
std::vector<std::string>& getClassTeachers() const;
void setClassGrades( std::vector<unsigned short>& vGrades );
std::vector<unsigned short>& getGrades() const;
// Notice That These Are Both Commented Out So The Compiler Will
// Define These By Default. And These Will Make Shallow / Stack Copy
// Student( const Student& c ); // Default Defined
// Student& operator=( const Student& c ); // Default Defined
};
The version of this class with its declaration by default will construct both a copy constructor and an equal operator.
class Student sealed {
private:
std::string m_strFirstName;
std::string m_strLastName;
std::vector<unsigned short> m_vClassNumbers;
std::vector<std::string> m_vTeachers;
std::vector<unsigned short> m_vClassGrades;
public:
Student( const std::string& strFirstName, const std::string& strLastName );
std::string getFirstName() const;
std::string getLastName() const;
void setClassRoster( std::vector<unsigned short>& vClassNumbers );
std::vector<unsigned short>& getClassRoster() const;
void setClassTeachers( std::vector<std::string>& vTeachers );
std::vector<std::string>& getClassTeachers() const;
void setClassGrades( std::vector<unsigned short>& vGrades );
std::vector<unsigned short>& getGrades() const;
private:
// These Are Not Commented Out But Are Defined In The Private Section
// These Are Not Accessible So The Compiler Will No Define Them
Student( const Student& c ); // Not Implemented
Student& operator=( const Student& c ); // Not Implemented
};
Where the second version of this class will not since I declared both of them as being private!
This is probably the best way I can demonstrate this. I only showed the header file interface to this class since the c++ source or code to be compiled is not of a concern. The difference in how these two are defined during the Pre-Compile Stage dictates how the Compiler Will Work before it begins to compile the source code into object code.
Keep in mind though that the standard library strings & containers do implement their own Copy Constructor & Assignment Operators! But the same concept applies to the behavior of the compiler if a class has basic types such as int, float, double, etc. So the compiler will treat a Simple class in the same manner according to its declaration.
class Foo {
private:
int m_idx;
float m_fValue;
public:
explicit Foo( float fValue );
// Foo( const Foo& c ); // Default Copy Constructor
// Foo& operator=( const Foo& c ); // Default Assignment Operator
};
Second Version
class Foo {
private:
int m_idx;
float m_fValue;
public:
explicit Foo( float fValue );
private:
Foo( const Foo& c ); // Not Implemented
Foo& operator=( const Foo& c ); // Not Implemented
};
The compiler will treat this class in the same manner; it will not define either of these since they will not be implemented due to being declared as private.
Related
#include "booking.h"
#include <iostream>
booking::booking ( const std::string p_title, const std::string p_notice, const category p_category, const person p_person, const booking::Type p_type, const double p_value ) :
m_type{ p_type },
m_title{ p_title },
m_notice{ p_notice },
m_person{ p_person },
m_category{ p_category },
m_value { p_value }
{
std::cout << "Booking was created" << std::endl; // Debug Message
}
These are the files (everything thats necessary to know in my opinion)
#pragma once
#include <string>
#include "person.h"
#include "category.h"
class booking
{
public:
enum Type { TYPE_REVENUE, TYPE_EXPENDITURE };
booking ( const std::string p_title, const std::string p_notice, const category p_category, const person p_person, const booking::Type p_type, const double p_value ); //Basic Constructor
~booking();
Type GetType ( );
std::string GetTitle ( );
std::string GetNotice ( );
category GetCategory ( );
double GetValue ( );
private:
Type m_type;
std::string m_title;
std::string m_notice;
category m_category;
person m_person;
double m_value;
};
If i put one of the class members (like m_type or the double value, it doesnt matter which) to const, it throws following error:
Fehler 1 error C2280: booking &booking::operator =(const booking &) : attempting to reference a deleted function C:\Program Files (x86)\Microsoft Visual C++ Compiler Nov 2013 CTP\include\utility 53
I dont get why the compiler complains about the copy constructor and whats basicly the matter.
You can't (reasonably) assign to an object of a class that has const members.
That's why you get an error about the copy assignment operator.
You're not getting a complaint about the copy constructor.
In other news:
In C++ ALL UPPERCASE names are a convention for macros. If they're used for anything else (e.g. constants, as in Java) then you increase the risk of name collisions and inadvertent text replacement. Besides it's an eyesore, read by many as extra heavy emphasis. Java doesn't have a preprocessor. C++ does have one.
It's a good idea to pass non-basic-type arguments in general as reference to const (you only added const). There are some extra considerations for large arguments that are copied. In C++11 these are best passed by value and moved.
Simple "getter" member function should be declared const so that they can be called on a const object.
Regarding the Java-inspired Get prefixes, consider GetSin(u)+GetCos(v) versus sin(u)+cos(v). In Java a Get prefix can have some value for tools that use introspection. Java has introspection. C++ doesn't have instrospection. The conventions employed should better be adapted to the language used.
When you declare a const member, the compiler does not generate a default assignment operator (it has no clue what to do with this member during assignment, after all, it is const ?), you will have to write the assignment operator yourself.
Note:
pass you parameters by reference to const.
operator= is not the copy-constructor, it is the assignment operator.
const objects cannot be updated, so in your assignment operator you cannot modify the object.
If you don't declare your own assignment operator, the compiler generates one for you which does member-wise copy. But if there is a const member, this doesn't work, so it can't generate the assignment operator after all. (In C++11 this is called having a deleted assignment operator).
Finally, if you have some code that tries to use the assignment operator, then you get this error about attempting to use the deleted assignment operator. Some of the standard library containers or algorithms require the assignment operator to be present. You didn't show all your code but somewhere you will have tried to perform an operation that requires assignment.
referring this thread, as to the copy constructor with const members,
the general form of the copy constructor is,
class Foo {
Foo( const Foo& f ) :
mem1( f.mem1 ), mem2( f.mem2 ) /* etc */
{}
};
where mem1 and mem2 are data members of Foo, which can be const
members, non-const members, const references or non-const references.
#include "booking.h"
#include <iostream>
booking::booking ( const std::string p_title, const std::string p_notice, const category p_category, const person p_person, const booking::Type p_type, const double p_value ) :
m_type{ p_type },
m_title{ p_title },
m_notice{ p_notice },
m_person{ p_person },
m_category{ p_category },
m_value { p_value }
{
std::cout << "Booking was created" << std::endl; // Debug Message
}
These are the files (everything thats necessary to know in my opinion)
#pragma once
#include <string>
#include "person.h"
#include "category.h"
class booking
{
public:
enum Type { TYPE_REVENUE, TYPE_EXPENDITURE };
booking ( const std::string p_title, const std::string p_notice, const category p_category, const person p_person, const booking::Type p_type, const double p_value ); //Basic Constructor
~booking();
Type GetType ( );
std::string GetTitle ( );
std::string GetNotice ( );
category GetCategory ( );
double GetValue ( );
private:
Type m_type;
std::string m_title;
std::string m_notice;
category m_category;
person m_person;
double m_value;
};
If i put one of the class members (like m_type or the double value, it doesnt matter which) to const, it throws following error:
Fehler 1 error C2280: booking &booking::operator =(const booking &) : attempting to reference a deleted function C:\Program Files (x86)\Microsoft Visual C++ Compiler Nov 2013 CTP\include\utility 53
I dont get why the compiler complains about the copy constructor and whats basicly the matter.
You can't (reasonably) assign to an object of a class that has const members.
That's why you get an error about the copy assignment operator.
You're not getting a complaint about the copy constructor.
In other news:
In C++ ALL UPPERCASE names are a convention for macros. If they're used for anything else (e.g. constants, as in Java) then you increase the risk of name collisions and inadvertent text replacement. Besides it's an eyesore, read by many as extra heavy emphasis. Java doesn't have a preprocessor. C++ does have one.
It's a good idea to pass non-basic-type arguments in general as reference to const (you only added const). There are some extra considerations for large arguments that are copied. In C++11 these are best passed by value and moved.
Simple "getter" member function should be declared const so that they can be called on a const object.
Regarding the Java-inspired Get prefixes, consider GetSin(u)+GetCos(v) versus sin(u)+cos(v). In Java a Get prefix can have some value for tools that use introspection. Java has introspection. C++ doesn't have instrospection. The conventions employed should better be adapted to the language used.
When you declare a const member, the compiler does not generate a default assignment operator (it has no clue what to do with this member during assignment, after all, it is const ?), you will have to write the assignment operator yourself.
Note:
pass you parameters by reference to const.
operator= is not the copy-constructor, it is the assignment operator.
const objects cannot be updated, so in your assignment operator you cannot modify the object.
If you don't declare your own assignment operator, the compiler generates one for you which does member-wise copy. But if there is a const member, this doesn't work, so it can't generate the assignment operator after all. (In C++11 this is called having a deleted assignment operator).
Finally, if you have some code that tries to use the assignment operator, then you get this error about attempting to use the deleted assignment operator. Some of the standard library containers or algorithms require the assignment operator to be present. You didn't show all your code but somewhere you will have tried to perform an operation that requires assignment.
referring this thread, as to the copy constructor with const members,
the general form of the copy constructor is,
class Foo {
Foo( const Foo& f ) :
mem1( f.mem1 ), mem2( f.mem2 ) /* etc */
{}
};
where mem1 and mem2 are data members of Foo, which can be const
members, non-const members, const references or non-const references.
When I learned C++ people told me to always implement at least rule of three methods.
Now I'm seeing the new "... = default;" from c++0x on stack overflow, and my question is:
Is there a c++11 standard implementation defined for those methods or is it compiler specific?
plus I would like to have some precisions:
What does the implementation looks like in term of code? (if it's generic)
Does this have an advantage compared to my example implementation below?
If you don't use assignment/copy constructor, what does *... = delete* do precisly, what's the difference with declaring them private? Answer (from #40two)
Is the new default= different from the old default implementation?
Disclaimer: when I'll need more advanced features in my methods, for sure I'll implements them myself. But I get used to implement assignment operator and copy constructor even when I never used them, just in order that the compiler don't.
What I used to do: (edited, #DDrmmr swap/move)
//File T.h
class T
{
public:
T(void);
T(const T &other);
T(const T &&other);
T &operator=(T other);
friend void swap(T &first, T &second);
~T(void);
protected:
int *_param;
};
//File T.cpp
T::T(void) :
_param(std::null)
{}
T::T(T &other)
: _param(other._param)
{}
T::T(T &&other)
: T()
{
swap(*this, other);
}
T &T::operator=(T other)
{
swap(*this, other);
return (*this);
}
friend void swap(T &first, T &second)
{
using std::swap;
swap(first._param, second._param);
}
T::~T(void)
{}
The default behavior is:
Default ctor ( T() ): calls bases def. ctors and members default ctors.
Copy ctor ( T(const T&) ): calls bases copy. ctors and members copy ctors.
Move ctor ( T(T&&) ): calls bases move. ctors and members move ctors.
Assign ( T& operator=(const T&) ): calls bases assign. and members assign.
Transfer ( T& operator=(T&&) ): calls bases transfer, and members transfer.
Destructor ( ~T() ): calls member destructor, and bases destructor (reverse order).
For built-in types (int etc.)
Default ctor: set to 0 if explicitly called
Copy ctor: bitwise copy
Move ctor: bitwise copy (no change on the source)
Assign: bitwise copy
Transfer: bitwise copy
Destructor: does nothing.
Since pointers are builtin types as well, this apply to int* ( not to what it points to).
Now, if you don't declare anything, your T class will just hold a int* that does not own the pointed int, so a copy of T will just hold a pointer to the same int. This is the same resulting behavior as C++03. Default implemented move for built-in types are copy. For classes are memberwise move (and depends on what members are: just copies for built-ins)
If you have to change this behavior, you have to do it coherently: for example, if you want to "own" what you point to, you need
a default ctor initializing to nullptr: this defines an "empty state" we can refer later
a creator ctor initializing to a given pointer
a copy ctor initializing to a copy of the pointed (this is the real change)
a dtor that deletes the pointed
an assign that deletes the pointed and receive a new copy of the pointed
.
T::T() :_param() {}
T::T(int* s) :_param(s) {}
T(const T& s) :_param(s._param? new int(*s._param): nullptr) {}
~T() { delete _param; } // will do nothing if _param is nullptr
Let's not define the assign, by now, but concentrate on the move:
If you don't declare it, since you declared the copy, it will be deleted: this makes a T object always being copied even if temporary (same behavior as c++03)
But if the source object is temporary, we can create an empty destination and swap them:
T::T(T&& s) :T() { std::swap(_param, s._param); }
This is what is called a move.
Now the assignment: before C++11 T& operator=(const T& s) should check against a self assignment, make the destination empty and receive a copy of the pointed:
T& operator=(const T& s)
{
if(this == &s) return *this; // we can shortcut
int* p = new int(s._param); //get the copy ...
delete _param; //.. and if succeeded (no exception while copying) ...
_param = p; // ... delete the old and keep the copy
return *this;
}
With C++11 we can use the parameter passing to generate the copy, thus giving
T& operator=(T s) //note the signature
{ std::swap(_param, s._param); return *this; }
Note that this works also in C++98, but the pass-by copy will not be optimized in pass-by move if s is temporary. This makes such implementation not profitable in C++98 and C++03 but really convenient in C++11.
Note also that there is no need to specialize std::swap for T: std::swap(a,b); will work, being implemented as three moves (not copy)
The practice to implement a swap function derives for the case where T has many members, being swap required in both move and assign. But it can be a regular private member function.
As i know, when we assign one object is another default copy constructor will be called.
class class1 obj1;
class class2 obj2;
obj1(obj2); //default copy constructor will be called by compiler
So, when should I write explicitly the copy constructor?
In your case the copy-assignment operator will be called, not the copy-constructor. To call the copy-constructor you would have do to e.g.
class1 obj1;
class1 obj2 = obj1; // Invokes the copy-constructor in obj2
A good idea when to write a copy-constructor (or a copy-assignment operator, or a destructor) you can see by reading about the rule of three. In short, if you have any of a destructor, copy-constructor or copy-assignment operator, then you should probably have all of them.
Also, while the compiler will auto-generate copy-constructor and copy-assignment operator for you if you do not provide your own, you have to remember that those auto-generated function will only do a shallow copy. If you have e.g. pointers to memory you allocate in the object, the auto-generated functions will only copy the actual pointer, and not what it points to. This means that after a copy you have two objects both pointing to the same memory. If you delete the pointer in the destructor, and one of the objects are destructed, the other object will still have its pointer, but it will now point to deleted memory.
You will get a default copy constructor when you don't write one, provided you don't add any of the three or five to your class : destructor, copy or move assignment or constructor.
Sometimes this does the right thing. For example, if you just need a shallow copy, or if the member's corresponding functions fo the right thing, for example smart pointers.
If an object exist prior to the assign, then it does not involve a construction but the assignment operator, signatures are:
T& operator=( T const & ); // from l-value ref
T& operator=( T && ); // from r-value ref, since c++11
A frequent strategy is to write the assignment operator as the idiom "copy and swap" :
T& operator=( T const & o ) {
T val( o ); // you need to write the copy ctor
swap(*this,o); // you need to write the swap
return *this;
}
T& operator=( T && o ) {
T val( std::move(o) ); // you need to write the move ctor
swap(*this,o); // you need to write the swap
return *this;
}
The c++11 version of that strategy
T& operator=( T o ) noexcept { // copy done before the operator that can be noexcept ( swap have to too)
swap(*this,o); // you need to write the swap
return *this;
}
In some cases you will find that the way your objects should be copied is not trivial.
If you consider the class :
class Car {
string BrandName;
int NumberOfPassenger;
}
Then it is clear that when you'll be copying two objects, you'll simply want to copy them member by member. There's nothing special to do here so the defaut copy constructor will work just fine.
But imagine that the class is instead :
class Car {
string BrandName;
int NumberOfPassenger;
Mechanics EngineeringStuff;
}
Here Mechanics is a reference type. What the copy constructor will do is simply copying the reference to the new object, so both cars - car1 and car2 - will share the same EngineeringStuff. But a more natural behaviour would be to allocate manually a new Mechanics object when performing the copy, so the cars don't share the same wheels, motors etc...
More generally, it's usually when you have to deal with reference types or certain kind of business logic that you will need to explicitly write your copy constructor.
I have never written copy constructor, so in order to avoid pain i wanted to know if what i have coded is legit. It compiles but i am not sure that it works as a copy constructor should.
Also do i have to use const in the copy constructor or i can simply drop it. (What i dont like about const is that the compiler cries if i use some non const functions).
//EditNode.h
class EditNode
{
explicit EditNode(QString elementName);
EditNode(const EditNode &src);
}
//EditNodeContainer.h
class EditNodeContainer : public EditNode
{
explicit EditNodeContainer(QString elementName);
EditNodeContainer(const EditNodeContainer &src);
}
//EditNodeContainer.cpp
EditNodeContainer::EditNodeContainer(QString elementName):EditNode(elementName)
{
}
//This seems to compile but not sure if it works
EditNodeContainer::EditNodeContainer(const EditNodeContainer &src):EditNode(src)
{
}
//the idea whould be to do something like this
EditNodeContainer *container1 = new EditNodeContainer("c1");
EditNodeContainer *copyContainer = new EditNodeContainer(container1);
A copy constructor is a constructor that has one of the following signatures:
class A
{
A(A& other);
//or
A(const A& other);
//or
A(volatile A& other);
//or
A(const volatile A& other);
//or any of the above + other parameters that have default arguments
//example:
A(const A& other, int x = 0) //this is also a copy constructor
};
The above is specified in 12.8.2 of the standard - C++03.
so you are implementing correctly a copy constructor.
The reason it should receive a const parameter is that you're not changing the object you're copying from. If you call non-const functions on it, you're doing something wrong.
Also, in your snippet
EditNodeContainer *container1 = new EditNodeContainer("c1");
EditNodeContainer *copyContainer = new EditNodeContainer(container1);
you're not calling a copy constructor, because you're passing an EditNodeContainer* as parameter, not a EditNodeContainer.
You're missing one * symbol. Copy constructor expects reference to object, but is given pointer to object. Just replace container1 with *container1 as parameter of copy constructor.
The parameter of a copy constructor can be an lvalue reference to non-const or an lvalue reference to const, but in practice it is always a reference to const (the deprecated auto_ptr is an exception).
You should not write a copy constructor unless you have to and you fully understand the consequences. If you are consequent in using RAII classes everywhere, you rarely need a custom copy constructor (unless you are writing a RAII class).
Also, please avoid raw pointers and new wherever possible.