I have a copy constructor that looks like this:
Qtreenode(const QtreeNode * & n) {
x=n->x;
y=n->y;
height=n->height;
width=n->width;
element=n->element;
}
I wrote this a week ago, and I looked back at it today and I was surprised by the line that when called the copy constructor with say swChild=new QtreeNode(*(n->swChild)); The arguement to the cc is a pointer by reference, right? But when I call it, I do (*(n->swChild)), which means the value of that child right? Why does my code work?
That is not a copy constructor. A copy constructor for a class is a constructor that takes a reference of the class's type. Any of the following would be copy constructors, though the second, taking a const reference, is by far the most commonly used:
QTreeNode(QTreeNode&);
QTreeNode(const QTreeNode&);
QTreeNode(volatile QTreeNode&);
QTreeNode(const volatile QTreeNode&);
Since this isn't a copy constructor, the implicitly declared copy constructor is still provided. It is declared as QTreeNode(const QTreeNode& n) and basically just copies each member of the class (i.e., it's a "shallow" copy, not a "deep" copy). It is this implicitly declared copy constructor that is used in your code.
That's not a copy constructor, it's just a regular old constructor. A copy constructor would take a const QtreeNode&, not a const QtreeNode*&.
Since you didn't define a copy constructor, the compiler made one for you. That's the one that is being called in the line swChild=new QtreeNode(*(n->swChild));, since you are passing a QtreeNode and not a pointer to one.
Related
Why use references to the parameters of the copy constructor?
I found a lot of information saying that it is to avoid unlimited calls, but I still can't understand it.
When you pass to a method by value, a copy is made of the argument. Copying uses the copy constructor, so you get a chicken and egg situation with infinite recursive calls to the copy constructor.
Response to comment:
Passing by reference does not make a copy of the object begin passed. It simply passes the address of the object (hidden behind the reference syntax) so the object inside the copy constructor (or any method to which an object is passed by reference) is the same object as the one outside.
As well as solving the chicken-and-egg here, passing by reference is usually (for larger objects - larger than the size of a point) faster.
Response to further comment:
You could write a kind of copy constructor that passed by pointer, and it would work in the same way as passing by reference. But it would be fiddly to call explicitly and impossible to call implicitly.
Declaration:
class X
{
public:
X();
X(const X* const pOther);
};
The explicit copy:
X x1;
X x2(&x1); // Have to take address
The implicit copy:
void foo (X copyOfX); // Pass by value, copy made
...
X x1;
foo (x1); // Copy constructor called implicitly if correctly declared
// But not matched if declared with pointer
foo (&x1); // Copy construcxtor with pointer might (?) be matched
// But function call to foo isn't
Ultimately, such a thing would not be regarded as a C++ copy constructor.
This code:
class MyClass {
public:
MyClass();
MyClass(MyClass c);
};
does not compile. That is, because the second line here:
MyClass a;
MyClass b(a);
should theoretically cause the infinite loop you're talking about - it should construct a copy of a to before calling the constructor for b. However, if the copy constructor looks like this:
MyClass(const MyClass& c);
Then no copies are required to be made before calling the copy constructor.
From this webpage
A copy constructor is called when an object is passed by value. Copy
constructor itself is a function. So if we pass an argument by value
in a copy constructor, a call to copy constructor would be made to
call copy constructor which becomes a non-terminating chain of calls.
Therefore compiler doesn’t allow parameters to be passed by value.
By passing the argument by value the copy constructor calls itself, entering in an infinite 'recursion cycle'. The link above explain pretty well the basic topics about the copy constructor.
So let's say I have a class called classname and this class has char* private data member.
In the class that I am taking, the professor wants us to do it like the one below, essentially creating a completely new array.
classname(const classname & source)
{
c_string = new char[strlen(source.c_string)+1];
strcpy(c_string, source.c_string);
}
But what if the two objects essentially share the same single array(that has already been created), like this one.
classname(const classname & source)
{
c_string = source.c_string;
}
I understand this doesn't necessarily do any 'copying' and having different pointers (that belong to different objects) might be risky. But is it considered a copy constructor? Or is it so that in order for it to be a copy constructor the second array must also be created?
From what I've been reading I feel like the second one is the case, but I need a definite NO THAT'S NOT A COPY CONSTRUCTOR to be completely over this in my mind.
Thank you.
As commenters have all correctly pointed out, what you have is a copy constructor, regardless of the function's content.
It's a copy constructor because the standard says so (see 12.8.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 (...).
Since you have a non template constructor for classname, whose signature matches const classname&, you have a copy constructor. Maybe not a great copy constructor, maybe a broken one which doesn't even copy, but a copy constructor nonetheless.
Since I can't say it better than those who have commented, I'll simply quote them:
Copy constructor is defined by its signature, not the content in the function body.- #liliscent
You are doing a shallow copy rather than a deep copy but it's still a copy-constructor.-#acraig5075
in 2 hours I'll take an exam on C++ and I'm still wondering about the syntax of the copy constructor.
So far what I understand is that you put the CopyConstructor in private so that when some function or whatever wants to copy your class it is not going to work because it will no have access to private variables. So you can check if you missed any pointers that will only make a shallow copy.
So far so good.
Now the syntax as I read it is
ClassName(const ClassName &)
and I wondered why you have that ampersand and the const in there. Would not just ClassName(ClassName) be enough?
From [class.copy]/2 in C++14:
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 .
So your suggestion would not be a copy-constructor, according to the definition of the language.
In fact your suggestion is ill-formed according to [class.copy]/6:
A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-qualified) X and either there are no other parameters or else all other parameters have default arguments.
In order to pass something by value, you need to be able to make a copy of it. How do we make a copy? The copy constructor of course! So if we define the copy constructor like ClassName(ClassName), we pass in a ClassName, call the copy constructor, which calls the copy constructor which...
This results in infinite recursion; not a very reasonable way of copying an object. That is why we pass by reference: so we don't need to make any other copies. The const is there because copying an object shouldn't modify it, otherwise you get something horrific like auto_ptr.
Additionally, with C++11 it is preferred to use =delete to stop people copying your class rather than making the copy constructor private:
ClassName (const Classname&) = delete;
const means you can also make a copy of a class that is constant (and you make the promise that you will not modify the class that you're copying.
The ampersand & means your copy constructor takes a reference to the thing it's trying to make a copy of (without the ampersand it would get a copy of it, which is impossible as we're actually trying to make a copy).
The syntax f(Classname) means that the argument is copied, while f(const Classname&) makes a reference to the argument.
It would however be quite bad to copy the argument which is provided to the copy constructor, which should define how such a copy is made...
I wondered why you have that ampersand and the const in there
The ampersand means that the object to copy is passed by reference. The const means that the reference can't be used to modify it; which is what you'd expect when copying it.
Would not just ClassName(ClassName) be enough?
That would mean the object is passed by value. To do that, it would have to be copied. To copy it, you'd have to call the copy constructor. To do that, you'd have to pass the object by value. To do that, it would have to be copied. And so on.
So no, a copy constructor can't take its parameter by value.
const ClassA& curShot = vec_shots[curShotIndx];
In the above code the curShot object is constructed and assigned at the same step. My question is which constructor is used in the above statement. I thought it will be the default constructor followed by the assignment operator, but it seems to call the copy constructor instead. Why is that the case?
What happens is that
vec_shots[curShotIndx];
returns a reference which you assign to const ClassA& curShot. There is no object creation involved in this step. Therefore no constructor is invoked (neither default nor copy constructor).
The assignment operator is not invoked either since you are not assigning one object instance to another, but only a reference. You are not handling more than one (existing) object instance in this code. So, no construction or assignment is invoked.
Since you wrote "it seems to call copy constructor", I assume the ampersand in your question is a typo.
In that case, if you would do
const ClassA curShot = vec_shots[curShotIndx];
it is evaluated as copy construction. It is just the same as the ugly const ClassA curShot( vec_shots[curShotIndx] ).
However, if you write
ClassA curShot; // I skipped the "const", because otherwise the example would be invalid.
curShot = vec_shots[curShotIndx];
then a default constructor gets called and an opearator= is called on the second line.
Moreover, "=" so much can mean calling NEITHER copy constructor NOR operator=, that you can have this:
const ClassA f(){ return ClassA(); }
//...
const ClassA curShot = f(); // we would expect here a copy constructor call
Here -- if the compiler uses return value optimization and usually it does -- only a default constructor gets called for curShot.
No constructor is used, curShot is a reference, an alias to an already existing object, not a stand-alone object by itself.
Also, initialization and assignment cannot be done at the same step. For example, say you had
ClassA original;
ClassA copy = original;
Here, copy is not assigned original, it's initalized using original. This is called copy initialization.
If you did
ClassA copy2(original);
this would be called direct initialization.
The copy constructor would be called in both instances. (copy elision can occur, so it might not be called, but it must be available)
Assignment is when you use operator = on an already existing object:
ClassA x;
ClassA y;
x = y; //assignment
This statement just define curShot as a reference, it's not a new object.
Can I write a copy constructor by just passing in a pointer instead of the const reference? (Would it be ok if I make sure that I am not going to change any values though?)
Like so:
SampleClass::SampleClass(SampleClass* p)
{
//do the necessary copy functionality
}
instead of:
SampleClass::SampleClass(const SampleClass& copyObj)
{
//do the necessary copy
}
Thanks in advance.
Thanks everyone. So, if I write a constructor that takes in a pointer( and thought that's my copy constructor), the compiler would still supply with the default copy constructor in which case my constructor( which i thought was my copy constructor) would not be called and the default copy constructor would be called. Got it.
Yes, you can write a constructor that takes a pointer to the object. However, it cannot be called a copy constructor. The very definition of a copy constructor requires you to pass an object of the same class. If you are passing anything else, yes, it's a constructor alright, but not a copy constructor.
You can write a constructor that takes a pointer as an argument.
But the copy constructor is the name we give a specific constructor.
A constructor that takes a reference (preferably const but not required) of the same class as an argument is just named the copy constructor because this is what it effectively does.
Besides the fact that it would not be a copy constructor and the compiler will generate the copy constructor unless you explicitly disable it, there is nothing to gain and much to loose. What is the correct semantics for a constructor out of a null pointer? What does this add to the user of your class? (Hint: nothing, if she wants to construct out of a heap object she can just dereference the pointer and use the regular copy constructor).
No. Copy constructors must take a reference, not pointer, if it's to be useful for passing-by-value, etc.
By definition, the copy ctor uses a const reference. While there is nothing stopping you from writing a ctor that takes a pointer, it raises some problems not present when using a reference - e.g., what should/can happen if a null pointer is passed in?
A copy constructor needs a reference because a value parameter would require making a copy, which would invoke the copy constructor, which would make a copy of its parameter, which would invoke the copy constructor, which ...
You can write a constructor like that but its not technically a copy constructor. For example the STL containers will still use the compiler generated copy constructor (the compiler generates one because you didn't write one).
The copy constructor is implicitly used in two cases:
When an instance of your class is passed by value to a function.
When an instance of your class is returned by value from a function.
As others have mentioned, you can write a constructor with the signature described (or with a const pointer), but it would not be used in either of the above cases.
You can write a perfectly valid copy constructor, and still be able to pass a reference that is NULL. You can test for NULL, but only if you do not use constructor initialization lists.
Example:
MyClass::MyClass( MyClass const& MyClassCopy )
: somevar( MyClassCopy.somevar ) // <- the init list goes here.
{
// If MyClassCopy is NULL, the initialization above is doomed!
// However we can check for NULL in the constructor body but
// the initialization list must be removed ...
if (&MyClassCopy == NULL ) throw( std::runtime_error("NULL pointer!"));
somevar = MyClassCopy.somevar;
}
// I'll now do some very dangerous programming to
// demonstrate one way that a NULL can get through ...
MyClass* P = NULL;
MyClass A( *P ); // Bang, you're dead!
As far as I know, there's no way to check for a NULL from inside the initialization list, so if you think you could end up with a situation where a NULL gets through, you have to test for it in the constructor body and do the initializing from there.
Don't forget there's a few gotchas with ::operator=() function to be aware of ...