[c++]: problem about initiation list in constructor - c++

I stumbled across a question that I never thought about before.
Here it is:
each object's (listed in the initialization list) "constructor" will be triggered.
class B
{
public:
B() { cout<<"B Con\n";}
B(const B &b) { cout<<"B Copy Con\n";}
};
class A
{
public:
A(B &b):_m(b) { cout<<"A Con\n";}
A(const A &a):_m(a._m) { cout<<"A Copy Con\n";}
private:
B _m;
}
main()
{
B b;
A a(b);
}
then I got the output as follows:
B Con
B Copy Con
A Con
According to the output, I think, 'A a(b)' triggered B's copy constructor.
If I got right, then that means 'A(B &b):_m(b)' triggers B's copy constructor.
Why not constructor but copy-constructor?

The reason is when you call
_m( whatever )
then the copy constructor
B(const B &b)
is the only one that could match the parameter list. You pass it one parameter and that parameter is of type class B.
Copy constructor is not something super special - it is just a parameterized constructor that will be invoked via the initialization list once the parameter list matches.

Because you're telling the compiler to initialize _m with b, how would that not call the copy constructor?

The answer lies in the A(B &b):_m(b) You are instantiating B _m with the copy constructor.
If instead you did A(B &b):_m() it would use the default constructor.

A(B &b):_m(b) { cout<<"A Con\n";}
Here _m(b) causes invocation of B(const B&) which is B's copy-constructor. That is why, it first prints B Copy Con when initializing _m, then it enters into A's constructor body, and prints A Con. That explains it all.

Related

Why is user defined copy constructor calling base constructor while default copy constructor doesn't? [duplicate]

This question already has answers here:
Why does the implicit copy constructor calls the base class copy constructor and the defined copy constructor doesn't?
(3 answers)
Closed 11 months ago.
Consider the following example:
class A
{
public:
A()
{
cout<<"constructor A called: "<<this<<endl;
};
A(A const& other) = default;
};
class B : public A
{
public:
B()
{
cout<<"constructor B called: "<<this<<endl;
};
//This calls A's constructor
B(B const& other)
{
cout<<"B copy constructor called: "<<this<<endl;
};
//While this doesn't call A's constructor
//B(B const& other) = default;
};
int main()
{
B b;
B b2(b);
cout<<"b: "<<&b<<endl;
cout<<"b2: "<<&b2<<endl;
return 0;
}
Output:
constructor A called: 0x7fffc2fddda8
constructor B called: 0x7fffc2fddda8
constructor A called: 0x7fffc2fdddb0
B copy constructor called: 0x7fffc2fdddb0
b: 0x7fffc2fddda8
b2: 0x7fffc2fdddb0
Why is the constructor of A is called when copying B?
Shouldn't the copy constructor of A be called instead?
However, if you change class B's copy constructor to be default, the constructor of A is not called when copying which makes sense.
It will be nice if someone can give a reasonable explanation as to why.
Why is the constructor of A is called when copying B? Shouldn't the copy constructor of A be called instead?
No, it shouldn't.
A derived class must always initialize a base class. If the derived class has a constructor that is implemented explicitly by the user, but it does not explicitly call a base class constructor in its member initialization list, the compiler will make an implicit call to the base class's default constructor, regardless of the type of the derived constructor. The compiler does not make any assumption about the user's intent in implementing the derived constructor. If the user wants a specific base class constructor to be called, they need to make that call themselves.
Since B has an explicitly implemented copy constructor that lacks a member initialization list, the compiler initializes A by calling its default constructor, not its copy constructor.
IOW, this:
B(B const& other)
{
...
}
Is equivalent to this:
B(B const& other) : A()
{
...
}
NOT to this, as you are thinking:
B(B const& other) : A(other)
{
...
}
However, if you change class B's copy constructor to be default, the constructor of A is not called when copying which makes sense.
Correct, a default'ed copy constructor will call the base class's copy constructor, not its default constructor. The compiler is implicitly implementing the entire derived constructor, and so it will choose the appropriate base class constructor to call.

Copy-constructor from base class

I have the following code:
#include <iostream>
#include <utility>
class A {
public:
A() { }
A(const A&, int i) { std::cout << "A copy" << std::endl; }
A(A&&, int i) { std::cout << "A move" << std::endl; }
A(const A&) = delete;
A(A&&) = delete;
};
class B : public A {
public:
B(int i) { std::cout << "B construct" << std::endl; }
B(const A& a) : A(a, 1) { std::cout << "B copy" << std::endl; }
B(A&& a) : A(std::move(a), 1) { std::cout << "B move" << std::endl; }
};
B make_b() {
return B(1);
}
int main() {
B b = make_b();
}
The compiler error reports the error that B cannot be copy-constructed (for return from make_b), because it has no copy-constructor, because A's copy-constructor is deleted.
Does B(const A&) not qualify as copy-constructor, and what is the rule that applies here?
Does the copy and move constructor always have to take one argument of the same type (and not a superclass)?
Can it have additional parameters with default values?
Can it be templated so that it can resolve to a valid copy constructor?
To allow implicit copy and move construction, is it necessary to explicitly add copy and move-constructors B(const B&) and B(B&&)?
Does B(const A&) not qualify as copy-constructor, and what is the rule that applies here?
No. A copy constructor creates another object of the same type. A is not the same type as B. If you try to construct an object of a derived class from an object of its base class, how are you supposed to initialize the derived class' members? The source object you are copying from doesn't have those members to copy!
Furthermore, B already has a copy constructor, implicitly declared by the compiler, but because the implicit definition would be ill-formed (because the base class A is not copyable) it is deleted by the compiler, so you cannot use it.
Does the copy and move constructor always have to take one argument of the same type (and not a superclass)?
Not necessarily one argument, B(const B&, int = 0) is a copy constructor, because it can be called to create a copy of a B. But B(const A&) is not a copy constructor.
Can it have additional parameters with default values?
Yes.
To allow implicit copy and move construction, is it necessary to explicitly add copy and move-constructors B(const B&) and B(B&&)?
Yes, you need to define them explicitly, because the implicit definitions the compiler would use won't work.
Since your derived type doesn't have any members, and you already have constructors that take an A, you could define them like so:
B(const B& b) : B(static_cast<const A&>(b) { }
B(B&& b) : B(static_cast<A&&>(b) { }
This creates delegating constructors which simply forward the argument to your existing constructors (using suitable casts to the base type).
About 1 :
when constructing , the compiler calls all the base classes one by one from the highest until the currently-constructed-class.
if C inherits from B inherits from A
the compiler calls A() then B() than C() ctros in order to build C object.
the same goes for copy constructors:
in your example , you called for A() copy constructor to build the "A" part of the object , but you deleted it .
the problem here is returing B by value, which calls first move ctor if exits , and move ctor if not. you deleted both

Why it works when I assign the object with others' object?

I'd like to assign the object of type B to object of type A, but i don't know why it works with different types for the assignment?
#include <stdio.h>
class B
{
public:
B()
{
printf("B default constructor.\n");
}
};
class A
{
public:
A()
{
printf("A Default constructor.\n");
}
A(B const& b) // if add the tag "explicit" for the constructor, it will not work...
{
printf("User constructor.\n");
}
A(const A& a)
{
printf("copy-constructor.\n");
}
void get(){printf("A::get\n");}
};
int main()
{
A a = B(); // What's the meaning to assign object of type B to object of type A?
Why it works with above line?
How it works when do this?
a.get();
}
Every constructor that can be called with a single argument defines an implicit conversion to a class type. So the constructor:
A(B const& b)
is a conversion constructor. If this type of conversion is not useful, you've found the answer: declaring it as explicit can prevent it:
explicit A(B const& b)
I think your problem comes from thinking it is assignment. But A a = B(); is initialization. a is not first created with default constructor and then assigned to, it is directly constructed. And while I don't have the standard reference handy, if you don't have the constructor but have the assignment operator overload, that line will not compile, because it is not assignemnt, it needs the right constructor. To have assignment, try
A a;
a = B(); // actually assigns A(B()) implicitly if no operator= overload
If all data members of a class can be assigned, then objects of the class can be assigned, so that's why a = A(); works without adding any code.
If you want to block the implicit conversion, make the constructor explicit (example in the other answer).

C++ inherited copy constructor call ?

I have class B derived from class A. I call copy constructor that I implemented myself for an object of class B. I also implemented myself a constructor for class A.
Is this copy constructor automatically called when I call copy constructor for class B ? Or how to do this ? Is this the good way:
A::A(A* a)
{
B(a);
// copy stuff
}
thanks!
You can do this with a constructor initialization list, which would look like this:
B::B(const B& b) : A(b)
{
// copy stuff
}
I modified the syntax quite a bit because your code was not showing a copy constructor and it did not agree with your description.
Do not forget that if you implement the copy constructor yourself you should follow the rule of three.
A copy constructor has the signature:
A(const A& other) //preferred
or
A(A& other)
Yours is a conversion constructor. That aside, you need to explicitly call the copy constructor of a base class, otherwise the default one will be called:
B(const B& other) { }
is equivalent to
B(const B& other) : A() { }
i.e. your copy constructor from class A won't be automatically called. You need:
B(const B& other) : A(other) { }

c++ is this copy constructor?

class A {};
class B { public: B (A a) {} };
A a;
B b=a;
I read this from http://www.cplusplus.com/doc/tutorial/typecasting/ . It says this is a implicit type conversion. From class A to class B.
I want to ask, is this also an example of copy constructor?
Thanks.
No, it's not a copy constructor. A copy constructor copies one object of one type into another of the same type:
B::B(const B& b)
{
// ...
}
As a side note, if you need a copy constructor then you also need a destructor and an assignment operator, and probably a swap function.
What B::B(A) is is a conversion function. It's a constructor that allows you to convert an object of type A into an object of type B.
void f(const B& obj);
void g()
{
A obja;
B objb = obja;
f(obja);
}
No, A copy constructor has the form
class A
{
public:
A(const A& in) {...}
}
No, a copy constructor is called when you create a new variable from an object. What you have there is two objects of different types.
The line B b = a; implies that a copy constructor is used, as if you had typed B b = B(a); or B b((B(a)));. That is, the compiler will check whether B has an accessible (public) copy constructor - whether user-defined or the default one provided by the compiler. It doesn't mean, though, that the copy constructor has to be actually called, because the language allows compilers to optimize away redundant calls to constructors.
By adding a user-defined copy constructor to B and making it inaccessible, the same code should produce a compiler error:
class A {};
class B {
public:
B (A ) {}
private:
B (const B&) {} // <- this is the copy constructor
};
A a;
B b=a;
For example, Comeau says:
"ComeauTest.c", line 10: error: "B::B(const B &)" (declared at line 6), required
for copy that was eliminated, is inaccessible
B b=a;
^