Different behaviour list initialization when specifying constructor - c++

I have code like this for example :
class A {
public:
int x;
A() {
std::cout << "Constructor Called !" << std::endl;
}
A(int y):x(y) {
std::cout << "Constructor Param Called !" << std::endl;
}
A(const A& copy) {
std::cout << "Copy Constructor Called !" << std::endl;
}
}
class B {
public:
A value;
//B(const A& val) : value(val){}
}
int main(){
B b { A(22)};
}
If i comment out the B constructor the output is just "Constructor Param Called", but if i uncomment B constructor the output would be "Constructor Param Called" & "Copy Constructor Called". My questions :
Why is the output different if i commented out the constructor ? (I've read about aggregate class & aggregate initialization, is this it ?)
What's the difference between aggregate initialization & direct initialization ?

When you remove the user-provided constructor for B, B becomes an aggregate. So aggregate-initialization is performed where each element of the class is copy-initialized from the elements of the initializer list. Since A(22) is a prvalue of the same class as B's element, copy-elision takes place where the value is stored directly into the object without any calls to the copy-constructor. This is new as of C++17.
When you declare the constructor for B, it is no longer an aggregate, so constructors are considered when you're doing an initialization.
Direct-intialization just means there is no = sign when you're initializing an object. Aggregate-initialization is what takes place when you're initializing an aggregate, and you can take a look at the definition on cppreference for that.

Related

How does the compile choose which constructor to call?

This is my code.
When I delete line 11, the output is
A(0)
B(0)
A(1)
about the last line, "A(1) ", why the second constructor of class A is called?
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "A(0)" << endl; }
A(const A& a) { cout << "A(1)" << endl; }
};
class B {
public:
B() : a() { cout << "B(0)" << endl; }
// B(const B& b) { cout << "B(1)" << endl; }
private:
A a;
};
int main() {
B object1;
B object2 = object1;
return 0;
}
A(0)
B(0)
A(1)
When
B(const B& b) { cout << "B(1)" << endl; }
is commented out/deleted the compiler generates a copy constructor for you. This provided copy constructor will copy all of the members of the class so in this case it will stamp out a copy constructor that looks like
B(const B& copy) : a(copy.a) {}
This is why you see a's copy constructor called.
When you do not comment out/delete
B(const B& b) { cout << "B(1)" << endl; }
You do not copy a because you do not tell it to do so. What the compiler does instead is creates a default initialization for it by transforming the constructor to
B(const B& b) : a() { cout << "B(1)" << endl; }
so the default constructor is called instead of the copy constructor.
The compiler is generating a copy constructor for you, which copies the member a. In order to copy member a, it calls its copy constructor in turn, which prints A(1).
Because object2 is initialized with the implicit copy constructor of B. An implicit copy constructor implicitly copy all the data members of the class, hence the call of the copy constructor of A, which prints "A(1)".
The issue you've run into has to do with thinking commenting out line 11 means you've deleted that constructor.
In C++, there are a couple of constructors that are automatically generated if you ended up using them, even if you didn't declare them yourself. The copy-constuctor, which has the same signature as the commented-out constructor in B, is one of them.
In your case, you end up first calling the default constructor for B, which first constructs it's member A using the default constructor as well. This should give the output you see, where the body of A's copy-constructor is reached before the body of B's because of member initialization ordering.
Then, you make a new object of type B using the assignment operator which implicitly calls the now-generated copy constructor of B. That means A's copy constructor gets called as well, which is a rule in how B's copy-constructor is auto generated. With A's copy-constuctor un-commented, it gets called with the printout.
The compiler for the class B (with the commented copy constructor) defines implicitly the default copy constructor that calls copy constructors for class members.
From the C++ 20 Standard (11.3.4.2 Copy/move constructors)
14 The implicitly-defined copy/move constructor for a non-union class
X performs a memberwise copy/move of its bases and members...
The implicitly defined default copy constructor of the class B looks like
B( const B &b ) : a( b.a )
{
}
Here is a demonstrative program
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "A(0)" << endl; }
A(const A& a) { cout << "A(1)" << endl; }
};
class B {
public:
B() : a() { cout << "B(0)" << endl; }
// An analogy of the implicitly declared copy constructor
B(const B& b) : a( b.a ){}
private:
A a;
};
int main() {
B object1;
B object2 = object1;
return 0;
}
The program output will be the same as if to remove the copy constructor of class B that corresponds to the implicitly generated copy constructor by the compiler.
A(0)
B(0)
A(1)

Invokation of principal constructor

The Standard defines three kinds of constructors:
— delegating constructor
— target constructor
— principal constructor
12.6.2/6:
The principal constructor is the first constructor invoked in the
construction of an object (that is, not a target constructor for that
object’s construction)
But the same section says:
Once the target constructor returns, the body of the delegating
constructor is executed
Since, a target constructor and a delegating constructor cannot be a principal constructor. So what one is? I would like to consider that by example:
#include <iostream>
using std::cout;
using std::endl;
struct A
{
int a;
A(int a)
{
cout << A::a << endl;
A::a = a;
}
A(int a, int b)
{
cout << A::a << endl;
A::a = a + b;
}
A() : A(10,10)
{
cout << "A()" <<endl;
}
};
A a; //Subsequence of constructor's body execution is A(int, int) --> A()
int main()
{
cout << a.a << endl;
}
demo
What is a principal in the example?
In your example you have
struct A
{
...
A(int a, int b)
{
...
}
A() : A(10,10) // A() is a delegating constructor and A(int,int) is the target constructor
{
...
}
};
A a;
This means A() is the delegating constructor, A(int,int) is the target constructor.
The standard says (N3690 §12.6.2 - 6)
The principal constructor is the first constructor invoked in the
construction of an object (that is, not a target constructor for that
object’s construction).
this means A() is both a principal and delegating constructor in your example and that A(int,int), since it's called by a delegating constructor, it's a target constructor and it cannot be a principal constructor.
TL;DR (as suggested by pqnet):
principal -> the one you invoke
delegating -> the one which calls another constructor
target -> the one that is called by another constructor
As an unrelated sidenote I agree with Joachim: you're default-initializing a nonstatic member variable and printing its value through scope resolution before its initialization. That is undefined behavior.
Consider the following example
#include <iostream>
int main()
{
struct A
{
A() : A( 10 ) {}
A( int x ) : A( x, 20 ) {}
A( int x, int y ) : x( x ), y( y ) {}
int x, y;
};
A a;
std::cout << "a.x = " << a.x << std::endl;
std::cout << "a.y = " << a.y << std::endl;
return 0;
}
In this example constructor A() is a delegating constructor. It calls constructor A( int ) that in turn also a delegating constructor that calls constructor A( int, int ).
In the definition
A a;
the first constructor that is invoked is A(). It is the principal constructor for this definition because it is called first. That is "The principal constructor is the first constructor invoked
in the construction of an object""
If you add one more definition to the body of main as for example
A a2( 5 );
then for this definition the principal constructor is A( int ).
You made a wrong conclusion that a delegating constructor can not be a principal constructor. The first delegating constructor in the chain of delegating constructors is the principal constructor.

Why constructor is not getting called

In this program "A's constructor called " is printing 2 times. My doubt is why "A's constructor called " is not printing with
A b = B::getA();
while getting printed with
A a;
In both the cases we are creating a new object.
Here is my Program:
#include <iostream>
using namespace std;
class A
{
public:
A() { cout << "A's constructor called " << endl; }
};
class B
{
static A a;
public:
B() { cout << "B's constructor called " << endl; }
static A getA() { return a; }
};
A B::a; // definition of a
int main()
{
A a;
A b = B::getA();
return 0;
}
The first constructor is called at program startup, when the static A within B is created.
The second constructor is called within your main when A is allocated on the stack.
There are no more calls to the default A constructor. When you call getA() a copy constructor will be used, which as you have not defined, will be provided by the compiler.
Because getA() calls a copy constructor, which you haven't written, so the default one will be called.
A b = B::getA();
That initialises b using the copy constructor. You're only tracing the default constructor, which isn't used here. If you wanted to trace the copy constructor too, then you'd need to provide one:
A(A const &) { cout << "A's copy constructor called " << endl; }
one contructore is getting called with "static A a" and "A B::a" i.e static initialization of a which is member of class B
during the execution of program in C, C++ all global and static variables initialized first.

Constructor call in inherited classes

Consider the following code:
class A {
public:
int a;
};
class B : public A {
public:
B() { std::cout << "B[" << a << "]" << std::endl; }
};
class C : public B {
public:
C() { std::cout << "C[" << a << "]" << std::endl; }
};
int main(int argc, char *argv[]) {
B();
std::cout << std::endl;
C();
}
And its output -- program compiled with g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3:
B[0]
B[-2097962768]
C[-2097962768]
The only way I found to get the second call -- C() -- to have its values initialized was adding an explicit call to the constructors, like:
class B : public A {
public:
B() : A() { std::cout << "B[" << a << "]" << std::endl; }
};
class C : public B {
public:
C() : B() { std::cout << "C[" << a << "]" << std::endl; }
};
Although I understand that calling the default constructor of each former class will initialize the values, I'm not able to see what is being called when nothing is specified.
Isn't the default constructor what is called by default -- hence its handle?
With your original code
class A {
public:
int a;
};
class B : public A {
public:
B() { std::cout << "B[" << a << "]" << std::endl; }
};
the members of A are not initialized, because you have not specified any initialization. And generally, in C++ you don't pay for what you don't need. So, you don't get initialization by default for POD members (however, you do get it for members that have constructors, because there the initialization is specified).
In your subsequent "explicit constructor call" code
class B : public A {
public:
B() : A() { std::cout << "B[" << a << "]" << std::endl; }
};
you have specified value initialization of the A base class sub-object. Effectively this reduces to zero-initialization.
It's the same difference as
A* p = new A;
versus
A* p = new A();
The latter value-initializes the object.
Standardese…
C++11 §8.5/10:
“An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.”
C++11 §8.5./7:
“To value-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized.
An object that is value-initialized is deemed to be constructed and thus subject to provisions of this International Standard applying to “constructed” objects, objects “for which the constructor has completed,” etc., even if no constructor is invoked for the object’s initialization.”
It is perhaps worth noting that value initialization was not part of the original C++98. It was introduced in C++03, by Andrew Koenig (of "Koenig look-up" fame) in order to deal with some severe problems of unexpected effects of pure default-initialization. Which is what the () initializer bought you in C++98.
Edit my original answer was quite wrong, so I have edited it heavily.
If you want to guarantee that A::a is zero-initialized in the absence of value initialization calls such as
A a1; //
or
B() { .... }
then you can give A a constructor and do the initialization there:
class A {
public:
A() : a() {} // value initialization of a, means zero initialization here.
int a;
};
Otherwise, explicitly calling A() in B and C's constructors performs the value initialization.
// value initialization of A sub-object leads to zero initialization of A::a
B() : A() {}
This also works for initialization of an A instance:
A a1 = A();

Constructors order

#include <iostream>
class A
{
public:
A() { std::cout << " A ctor" << std::endl; }
A(int i) { std::cout << " A ctor i" << std::endl; }
~A() { std::cout << " A dtor" << std::endl; }
};
class B: public A
{
public:
B() : A () { std::cout << " B ctor" << std::endl; }
~B() { std::cout << " B dtor" << std::endl; }
};
class C: public A
{
public:
B _b;
C() : _b (), A () { std::cout << " C ctor" << std::endl; }
~C() { std::cout << " C dtor" << std::endl; }
};
int main ()
{
C c;
}
The output is:
A ctor
A ctor
B ctor
C ctor
C dtor
B dtor
A dtor
A dtor
What is the order of the init. list? Why, in the init. list of C, ctor of A called before ctor of B? I thought the output should be:
A ctor
B ctor
A ctor
C ctor
C dtor
A dtor
B dtor
A dtor
Thanks.
The order in which you write initializations in the initialization list is not important, the order of initialization is determined independently of that list by other rules:
First the base class is initialized. That's why in the construction of C the base class constructor A is called first. Everything that belongs to the base class is constructed in this step (base classes and member variables belonging to the base class), just like when a normal object of that base class would be constructed.
Then the member variables of the derived class are initialized, in the order in which they are declared in the class. So if there are several member variables, the order in which they are declared determines the order in which they are initialized. The order of an initialization list is not important.
Base class constructors are called before derived class constructors. This allows the derived class to use members in the base class during their construction.
During destruction, the opposite is true. Subclass destruction occurs before the base class, for exactly the same reason.
If you think about it - it makes perfect sense. The base class has no knowledge of the subclass, but the opposite is not true. This determines the ordering in order for everything to work as expected.
I think your confusion is why is C's initialization list processed right to left instead of left to write. It is because, the compiler processes parameters in "last in first out" fashion. Hence the order: First A's c'tor. Since _b is an object of B which is derived from A, base class is constructed before the derived class so A c'tor is called and then B's c'tor. And finally, C's c'tor is called. When C's object is destructed, it follows the reverse order.
If you use GNU compiler, -Wall option will help you.