Tuesday, June 12, 2007

2.1 Default Constructor Construction

"Default constructors...are generated (by the compiler) where needed..." Needed by whom? To do what?

Global objects are guaranteed to have their associated memory "zeroed out" at program start-up. Local objects allocated on the program stack and heap objects allocated on the free-store do not have their associated memory zeroed out; rather, the memory retains the arbitrary bit pattern of its previous use.

When is a default constructor synthesized, then? Only when the implementation needs it.

The standard states the following:

If there is no user-defined constructor for class X, a default constructor is implicitly declared... A constructor is trivial if it is an implicitly declared default constructor...

A nontrivial default constructor is one that is needed by the implementation and, if necessary, is synthesized by the compiler. There are four conditions under which the default constructor is nontrivial.


1) Member Class Object with Default Constructor
2) Base Class with Default Constructor
3) Class with a Virtual function
4) Class with a Virtual Base Class

1) Member Class Object with Default Constructor

If a class without any constructors contains a member object of a class with a default constructor, the implicit default constructor of the class is nontrivial and the compiler needs to synthesize a default constructor for the containing class.

class Foo { public: Foo(), Foo(int) ...};
class Bar { public: Foo foo; char *str;};
void foo_bar()
{
Bar bar; // bar::foo must be initialized here...
if ( str ) { } ...
}

The synthesized default constructor contains the code necessary to invoke the class Foo default constructor on the member object Bar::foo, but it does not generate any code to initialize Bar::str. Initialization of Bar::foo is the compiler's responsibility; initialization of Bar::str is the programmer's.

inline Bar::Bar()
{
//Pseudo C++ code
foo.Foo::Foo();
}

The sythesized default constructor meets only the needs of the implementation, not the needs of the program. If the programmer provides for the initialization of str via the following default constructor:

//Programmer defined default constructor
Bar::Bar () { str = 0;}

Because the default constructor is explicitly defined, the compiler cannot synthesize a second instance to do its work. In this case, the compiler augments the existing constructors, inserting code that invokes the necessary default constructors prior to the execution of the user code.

//Augment default constructor
Bar::Bar ()
{
foo.Foo::Foo(); //augmented compiler code
str = 0; //explicit user code
}

For multiple class members requiring constructor initialization, the compiler will insert code within each constructor, invoking the associated default constructors for each member in the order of member declaration. This code is inserted just prior to the explicitly supplied user code.

2) Base Class with Default Constructor

The synthesized default constructor of the derived class invokes the default constructor of each of its immediate base classes in the order of their declaration.

What if the designer provides multiple constructors but no default constructor? The compiler will augment each constructor with the code necessary to invoke all required default constructors.

3) Class with a Virtual function

There are two additional cases in which a synthesized default constructor is needed:
3.1 The calss either declares (or inherits) a virtual function
3.2 The class is derived from an inheritance chain in which one or more base classes are virtual.

The following two class "augmentations" occur during compilation:

1. A virtual function table is generated and populated with the addresses of the active virtual functions for that class. 如何判定一个方程是 active的呢?

2. Within each class object, an additional pointer member (the vptr) is synthesized to hold the address of the associated class vtbl.

widget.flip()
{
( * widget.vptr[1]) (&widget);
}

In classes that do not declare any constructors, the compiler synthesizes a default constructor in order to correctly initialize the vptr of each class object.

4) Class with a Virtual Base Class

We need to make the virtual class location within each derived class object available at runtime. All reference and pointer access of a virtual base class is achieved through the associated pointer. For each constructor the class defines, the compiler inserts code that permits runtime access of each virtual base class.

There are four characteristics of a class under which the compiler needs to synthesize a default constructor for classes that declare no constructor at all. The Standard refers to these as implicit, nontrivial default constructors. The synthesized constructor fulfills only an implementation need. It does this by invoking member object or base class default constructors or initializing the virtual function or virtual base class mechanism for each object. Classes that do not exhibit these characteristics and that declare no constructor at all are said to have implicit, trivial default constructors. In practice, these trivial default constructors are not synthesized.

Within the synthesized default constructor, only the base class subobjects and member class objects are initialized. All other nonstatic data members, such as integers, pointers to integers, arrays of integers, and so on, are not initialized. These initializations are needs of the program, not of the implementation. If there is a program need for a default constructor, such as initializing a pointer to 0, it is the programmer's responsibility to provide it in the course of the class implementation.

Programmers new to C++ often have two common misunderstandings:

  1. That a default constructor is synthesized for every class that does not define one

  2. That the compiler-synthesized default constructor provides explicit default initializers for each data member declared within the class

As you have seen, neither of these is true.

No comments: