Pergunta

I know that default initialization for non-POD types will also default initialize non-static non-POD member variables by calling their default constructor. But I'm not sure exactly how this happens. Here is an example of what I mean:

#include <iostream>
#include <vector>
using namespace std;

class Test2 {
  public:
    Test2() {cout <<"Here";}
};

class Test {
  public:
    Test() {}
    Test2 i;
};

int main() {
  Test foo;
}

The output is:

Here

Based on the C++ standard on initializers (8.5), for default initialization:

— if T is a non-POD class type (clause 9), the default constructor
for T is called (and the initialization is ill-formed if T has no
accessible default constructor);

So given this, I do expect that the default constructor Test() will get called, but my empty default constructor for the class Test does not initialize Test2 i explicitly yet clearly, Test2() is getting called implicitly somehow. What I'm wondering is how this happens?

Similarly, for value initialization (not related to example above), if an empty user defined default constructor does not explicitly zero initialize a POD non-static member variable, how does that variable get zero initialized (which I know it does do)? Since based on the standard, it seems that for value initialization, all that happens when you have a user defined default constructor is that the constructor gets called.

The corresponding part of the C++ standard for value initialization is the following:

— if T is a class type (clause 9) with a user-declared constructor (12.1), then the   
default constructor for T is called (and the initialization is ill-formed if T has no 
accessible default constructor);

This question is similar to c++ empty constructor and member initialization But the difference is that instead of asking what the end result behavior is, I'd like to know why the end result behavior happens.

Foi útil?

Solução

In the C++11 standard, section 12.6 paragraph 8:

In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then

  • if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
  • otherwise, if the entity is a variant member (9.5), no initialization is performed;
  • otherwise, the entity is default-initialized (8.5).

You are encountering the third case, where there is no initializer for the member and the member isn't a variant member, so in that case it is default-initialized.

Also, from paragraph 10:

In a non-delegating constructor, initialization proceeds in the following order: - First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

  • Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
  • Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
  • Finally, the compound-statement of the constructor body is executed.

Regardless of what you specify in your constructor, the members are going to be initialized just before the body of the constructor is executed.

A mem-initializer-id is the identifier used to refer to a member in a constructor initializer list:

class Test {
  public:
    Test() : i() {} // Here `i` is a mem-initializer-id
    Test2 i;
};

Outras dicas

Value initialization of a type with a user-defined default constructor performs no initialization on non-static POD members if they are not explicitly initialized in the constructor. E.g., in this program:

#include <iostream>

using namespace std;

struct Foo {
    // Foo has a user-defined default constructor
    // that does not initialize i
    Foo() {}
    int i;
};

int main() {
    Foo x{}; // value-initialize x
    cout << x.i << endl;
}

x.i is uninitialized. The program therefore technically has undefined behavior, but in this case "undefined behavior" most likely means that it will print an unspecified integer value that is likely not 0.

Language lawyer argument:

  • §12.6.1p2: "An object of class type can also be initialized by a braced-init-list. List-initialization semantics apply; see 8.5 and 8.5.4."
  • §8.5.4p3: "List-initialization of an object or reference of type T is defined as follows: ... If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized."
  • §8.5p7: "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)

According to the draft of the C++ standard found at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf, section 12.6.2:

If a given non-static data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then

— If the entity is a non-static data member of (possibly cv-qualified) class type (or array thereof) or a base class, and the entity class is a non-POD class, the entity is default-initialized (8.5). If the entity is a non-static data member of a const-qualified type, the entity class shall have a user-declared default constructor.

— Otherwise, the entity is not initialized. If the entity is of const-qualified type or reference type, or of a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of a const-qualified type, the program is ill-formed.

In other words, if an object of a non-POD class type does not appear in an initializer list, the compiler interprets this as if the object had appeared with its default constructor being called.

Also, note that other types (i.e. primitives and POD types) are not initialized, which is different from what you indicated in your question. Global objects are zero-initialized, but the same isn't true for objects on the stack. Here is a small program I put together to test this:

#include <iostream>

class T
{
public:
    T() {}

    void put(std::ostream &out)
    {
        out << "a = " << a << std::endl;
        out << "b = " << b << std::endl;
        out << "c = " << c << std::endl;
    }
private:
    int a;
    int b;
    int c;
};

T t2;

int main()
{
    T t;
    t.put(std::cout);
    t2.put(std::cout);

    return 0;
}

Compiling with g++ 4.5.2, I got the following output:

a = 8601256
b = 3
c = 2130567168
a = 0
b = 0
c = 0
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top