c++ Using const in a copy constructor? - c++

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.

Related

Is there any way to call the move assignment operator in C++ without std::move?

I wonder if it is possible to call the move assignment operator without using the standard
library std::move.
So for example if I call :
class A {
private:
char* data;
public:
// move constructor
A(A&& other) : data(other.data) {
// we steal the pointer!
other.data = NULL;
}
// move operator=
A& operator=(A&& other) {
if (this != &other) {
delete data;
data = other.data;
data.foo = NULL;
}
return *this;
}
};
int main(){
A objA;
A objB;
objB = std::move(objA);
}
can I now write something other than objB = std::move(objA); ?
Is there any way to call the move assignment operator in C++ without std::move?
std::move is a very simple function that merely performs a cast. You can write that cast instead of calling the function:
objB = static_cast<A&&>(objA);
I don't know of a practical reason for doing this.
Assuming you want to keep objA as a named local variable then, no, there is no way to pass it to a constructor of objB and have the constructor being invoked be the move constructor, short of using std::move (or equivalent things that similarly return T&& to make the expression be an rvalue T, like a static_cast<A&&>).
That's by design. It should not be possible to accidentally move from a named, declared object; you should have to ask for it. That's why std::move is called std::move, despite not actually performing any move itself: its name strongly signals the usual intent of using it.
The other way to invoke a move constructor is to pass an expression that was never going to be an lvalue in the first place, such as one that creates a temporary.

Can I use a `unique_ptr` in a vector, or do I need to switch to `shared_ptr`?

Given this class with a unique_ptr:
class MyClass
{
public:
MyClass(){}
MyClass(MyClass &&other) : ptr(std::move(other.ptr)){}
std::unique_ptr <int> ptr;
};
Is there any way to make it possible to have a std::vector<MyClass>?
void ThisBreaksIt()
{
MyClass instance;
std::vector<MyClass> mv;
mv.push_back(instance);
}
As-is, this gives me the error
error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
This makes sense, since I have no copy constrctor, and when the compiler tries creating a default copy constructor, it tries copying the unique_ptr, which isn't allowed.
I can make it compile by adding this constructor:
MyClass(const MyClass&){}
But of course, this leaves the unique_ptr uninitialized, and is not what I want.
I can't add
MyClass(const MyClass& other) : ptr(std::move(other.ptr)){}
because it's const, and I cant call std::move() on a const object. I can create the constructor
MyClass(MyClass& other) : ptr(std::move(other.ptr)){}
but doesn't solve the original compile error, as vector::push_back uses a const copy constructor.
So, I'm stuck. Is there a way to do what I'm trying to do?
All of these issues go away if I just use a shared_ptr instead of unique_ptr. Is that what I should be doing?
There's no problem storing a non-copyable type in a vector; it's only required to be movable, as yours is.
The problem is that this:
mv.push_back(instance);
tries to insert a copy of instance, and the class is not copyable. But it is movable:
mv.push_back(std::move(instance));
Note that there's no need to write your own default and move constructors in this example. The implicit ones will do the exactly what yours do.
You can use emplace_back. This only allows adding rvalues.
Therefore do this:
void ThisShouldFixIt()
{
mv.emplace_back(MyClass());
}
Note that emplace_back can call the applicable constructor
of T implace, as mentioned below. For what you are doing
push_back is fine though, given that a version taking an rvalue
reference does exist.

c++ copy constructor signature : does it matter

My current implementation uses lots of copy constructors with this syntax
MyClass::Myclass(Myclass* my_class)
Is it really (functionnaly) different from
MyClass::MyClass(const MyClass& my_class)
and why?
I was adviced that first solution was not a true copy constructor. However, making the change implies quite a lot of refactoring.
Thanks!!!
It's different in the sense that the first isn't a copy constructor, but a conversion constructor. It converts from a MyClass* to a MyClass.
By definition, a copy-constructor has one of the following signatures:
MyClass(MyClass& my_class)
MyClass(const MyClass& my_class)
//....
//combination of cv-qualifiers and other arguments that have default values
12.8. Copying class objects
2) A non-template constructor for class X is a copy constructor if its
first parameter is of type X&, const X&, volatile X& or const volatile
X&, and either there are no other parameters or else all other
parameters have default arguments (8.3.6).113) [ Example: X::X(const
X&) and X::X(X&,int=1) are copy constructors.
EDIT: you seem to be confusing the two.
Say you have:
struct A
{
A();
A(A* other);
A(const A& other);
};
A a; //uses default constructor
A b(a); //uses copy constructor
A c(&a); //uses conversion constructor
They serve different purposes alltogether.
The first version is not a copy constructor. Simple as that. It's just another constructor.
A copy constructor for a class X must have signature (X &, ...) or (X const &, ...) or (X volatile &, ...) or (X const volatile &, ...), where all arguments but the first have default values if they are present (and it must not be a template).
That said, you should think very carefully about why you're violating the Rule of Zero: Most well-designed classes shouldn't have any user-defined copy-constructor, copy-assignment operator or destructor at all, and instead rely on well-designed members. The fact that your current constructor takes a pointer makes me wonder if your code behaves correctly for something like MyClass x = y; — worth checking.
Certain language constructs call for a copy constructor:
passing by value
returning by value
copy-style initialization (although the copy is often elided in that case)
If the language calls for a copy, and you have provided a copy constructor, then it can be used (assuming the cv-qualifications are OK, of course).
Since you have not provided a copy constructor, you will get the compiler-generated copy constructor instead. This works by copying each data member, even if that's not the right thing to do for your class. For example if your not-a-copy-constructor explicitly does any deep copies or allocates resources, then you need to suppress the compiler-generated copy.
If the compiler-generated copy works for your class, then your not-a-copy-constructor is mostly harmless. I don't think it's a particularly good idea, though:
void some_function(const MyClass &foo);
MyClass *ptr = new MyClass();
const MyClass *ptr2 = ptr;
some_function(ptr); // compiles, but copies *ptr and uses the copy
MyClass bar(ptr2); // doesn't compile -- requires pointer-to-non-const
Even assuming that the compiler-generated copy is no good for your class, making the necessary change need not require a lot of refactoring. Suppose that your not-a-constructor doesn't actually modify the object pointed to by its argument, so after fixing the signature you have:
MyClass::MyClass(const MyClass* my_class) {
// maybe treat null pointer specially
// do stuff here
}
You need:
MyClass::MyClass(const MyClass& my_class) {
do_stuff(my_class);
}
MyClass::MyClass(const MyClass* my_class) {
// maybe treat null pointer specially
do_stuff(*my_class);
}
MyClass::do_stuff(const MyClass& my_class) {
// do stuff here
}
You also need to copy any initializer list from the old constructor to the new one, and modify it for the fact that my_class isn't a pointer in the new one.
Removing the old constructor might require a lot of refactoring, since you have to edit any code that uses it. You don't have to remove the old constructor in order to fix any problems with the default copy constructor, though.
The first example is not a copy constructor. This means that when you provide it, the compiler still provides you with a default copy constructor with signature equivalent to
MyClass(const MyClass& my_class);
If you are doing something special with your constructor, and the compiler provided copy constructor does not follow that logic, you should either implement a copy constructor or find a way to disable it.
Why would I want to use a copy constructor instead of a conversion constructor?
It can be problematic if you give MyClass objects to some code that expect your copy-constructor to be valid.
This is the case for STL containers. For instance, if you use a std::vector<MyClass>, you must be aware that vectors are allowed to move elements around for reallocation using their copy constructors.
The default constructor provided by the compiler will perform a shallow copy, calling copy constructors of every attributes, making simple copies for base type like pointers. If you want some form of deep copy you will have to properly rewrite the copy constructor of MyClass

C++, Assignment to class instance from a function call?

I understand, or at least have an Idea of, why the following code does not work:
class Spambar {
public:
Spambar() {};
Spambar(Spambar& sb) {};
Spambar operator + (Spambar sb) {
Spambar new_sb;
return new_sb;
}
};
int main() {
Spambar sb1;
Spambar sb2;
Spambar sb3 = sb1 + sb2; // <<< Error: "No matching function for call to ... "
}
I guess, the problem is that the copy-constructor expects a reference to a Spambar instance. As no reference but a shallow instance is returned, the compilation fails.
So, how do I get that to work?
The problem is that the result of sb1 + sb2 is a temporary; the copy constructor used to initialise sb3 requires a non-const reference; and you can't take a non-const reference to a temporary.
You almost certainly want to fix this by changing the constructor's parameter type to Spambar const &. While you're at it, you should almost certainly do the same to operator+, and also make the operator itself const:
Spambar(Spambar const &);
Spambar operator + (Spambar const &) const;
If you're doing something very strange, and actually want the copy-constructor to modify its argument, then you'll have to either avoid passing temporaries to it, or do some nasty hackery with mutable or const_cast. In C++11, you would use a move constructor, with parameter type Spambar &&, for this sort of thing.
Your class does not have a copy constructor taking a const reference. Normally, a copy constructor looks like:
Spambar(const Spambar&);
The form you show is used in only very rare circumstances, and it is probably preventing your code from working.

Copy constructor converts from const to non-const?

Consider the following :
class A
{
public:
int xx;
A(const A& other)
{
cout << "A cctor" << endl;
/* do some stuff */
}
A(int x) : xx(x) {} /* conversion constructor */
};
int main()
{
A a = 1;
A other = a;
return 0;
}
Is it right to say that CCtor converts from const to non-const in this case (and also in general) ?
Thanks ,Ron
A copy constructor creates a new copy of an existing object, that object may or may not be const. The const in A::A(const A& other) just says we are not going to change other in the copy ctor. Indeed if you attempt to modify other inside the ctor the compiler will moan at you.
The created object also may or may not be const depending on how you declare it.
No idea what you mean. A(const A&) is a typical copy-ctor, which has a "read-only" access to its only argument. If you pass anything const, everything is fine. If you pass anything non-const, for ctor it becomes const. A a = 1 is a conversion ctor, as you said. A other = a is a copy ctor. What's the question?
Regarding your question's title, in C++ there's no fair way to convert const to non-const.
class A
{
public:
int xx;
A(const A& other)
{
cout << "A cctor" << endl;
/* do some stuff */
// other is const here - you can only call its
// const methods and read all its data members
}
A(int x) : xx(x) {} /* conversion constructor */
// at this point, x is not const, but it's a copy
// of an object you've passed here, not the object itself
// you can change x, it just doesn't matter - you
// change the copy
};
int main()
{
A a = 1; // a is not const, 1 is passed "by value", since it's primitive type
A other = a; // a is not const, other is not const, a is passed by const reference
return 0;
}
Constructor initializes a new copy. And there is no problem in copying from a constant.
No conversion is involved.
What do you mean by CCtor converts from const to non-const?
If you mean, the non-const object gets created from the const object by invoking the copy-constructor, then yes. But that doesn't mean the const-object itself becomes non-const inside the copy-constructor (or at the call site). It only means that the newly constructed object is created by copying the existing object which is passed as const reference to the copy-constructor.
No the copy constructor creates an copy of the class object by taking another class object as the parameter.
Since in order to construct the new object being passed as parameter is not required to be modified it it passed as an const.
No, it's not converting to a non-const object. Consider this:
A a(42);
const A another = a;
Here, another is a const object, created from a non-const object.
More important, however, is that a constructor creates a new object from an existing one. Whether that new object is const or not does not depend on the existing object. All four possible combinations of const/non-const old/new objects are possible.
In the sense that the A(int) constructor converts from int to A, yes it's true that your copy ctor A(const A&) "converts" from const A to A. For that matter it also "converts" from non-const A to A, since the const reference parameter can bind to either.
And since the same constructor is used to create a const object as to create a non-const one, that copy ctor can also "convert" from A or const A to const A.
I've used "convert" in quotes just because converting from a type to itself or a cv-qualified version of itself is perhaps a confusing use of the term, normally you just call that "copying" rather than conversion.
The constructor parameter can bind to an instance of a derived class of A too, so you might say that it converts derived classes to A. That's normally called "slicing".
Strictly speaking it's not the copy ctor itself that converts anything, but a conversion to A (whether a cast or an implicit conversion) does depend on using a matching constructor. So I suppose the constructor can claim a large part of the credit.