C++ Default Constructors and Object Initialization

by Cliff Green, Copyright © 1995, 1998

This page last
updated on:

May 16, 2001

Contents and Related Links

CodeWrangler Pages

Overview

I consider object initialization in C++ to be one of the most important concepts to understand and to become proficient in. Everyone who designs and implements C++ classes and class libraries should be expert in the fundamental rules and details of how objects are created and initialized, according to the language standards.

One reason that understanding object creation and initialization is so important is that there are critical performance implications. I'm fond of using the term "under the hood", and the C++ language will make sure that some kind of initialization is performed, typically "under the hood", even if it's unnecessary or not the wanted form of initialization.

To help in understanding object initialization, this Web page gives a short overview of constructors (default and regular), member data initialization, and base object initialization.

Constructors

This page assumes basic knowledge of constructors. Keep in mind the restrictions on constructors - they cannot be virtual, static, const or volatile functions, and are not inherited. Also, an object of a class with a constructor cannot be a member of a union, cannot specify a return type (not even void, although a return statement without a return value may be used), and the address of a constructor function cannot be taken.

An important note - the constructor is the only way to initialize and modify const objects. This includes both const objects created at the application level, and const objects that are included in a class as member data.

Example:

const Xyz xyzObj(arg1, arg2);  // ctor with arg1, arg2 is only way xyzObj
                               // can be initialized and modified
cout << xyzObj.fct1(some_arg); // must be a const member fct

class Foo {
public:
  Foo (int x, int y) : mXyz (x, y)  // only way to init or modify mXyz
    { /* Foo ctor code */ }
private:
  const Xyz mXyz;
};

Foo fooObj (arg1, arg2);  // member data mXyz is init'ed appropriately
Foo anotherFooObj;        // Error - no default ctor (see next section)
Remember that the member initialization list, used to initialize base and member data objects, is in the definition, not declaration of the constructor. The preceding example had both the declaration and definition written together in the header file for brevity.

Default Constructors

A default constructor is a constructor taking no arguments (or a constructor with all the arguments having defaults). Default constructors and copy constructors are generated by the compiler, when appropriate. Generated default constructors are public.

Important note: If one or more constructors are created by the programmer, then the compiler will not create a default constructor. Usage that requires a default constructor will generate a compiler error if the programmer has not explicitly declared (and later defined) a default destructor.

Example:

class Xyz { /*  ... no default ctor */ };
class Foo {
public:
  Foo (int i, int j) { /* ctor code */ }  // NOTE - no default ctor
  // ...
};
class Mnm {
public:
  Mnm () { /* default ctor code */ }
  //...    // other ctors and member fcts as needed
};


Xyz xyzObj;  // default ctor generated
Foo fooObj;  // ERROR - user needs to provide default ctor
Mnm chocFlavor; // user specified default ctor is invoked
This is specially important to remember when new'ing an array of objects - unless a default constructor is defined for the class, the objects cannot be created and initialized. It is also necessary for any member data objects that are not explicitly initialized through one of the enclosing class constructors member initialization list. If the member data object is not explicitly initialized and it has one or more constructors defined, one of them must be a default constructor. If there are no constructors defined for the member data object, the compiler will generate one.

From the C++ draft standard (December 1996 Working Paper):

If there is no user-declared constructor for class X, a default constructor is implicitly declared. An  implicitly-declared default constructor is an inline public member of its class.

Object Initialization

While the default constructor must be used when new'ing arrays of objects, if the array is allocated statically or on the stack, there is a way to specify arguments for each object using the brace initializer syntax.

Example:

class Foo {
public:
  Foo ();
  Foo (int i);
  Foo (int i, int j);
  // ...
};

Foo a[6] = {10, Foo(3,4), Foo(), 22};
  // a[0] inited with Foo(10), a[1] with Foo(3,4), a[2] with Foo(), a[3] with
  // Foo(22), and the rest with the default Foo constructor
For completeness and compatibility with ISO/Ansi C, a class with no constructors, no private or protected members, no base classes, and no virtual functions (which is equivalent to a traditional C struct - the reference manuals call this an aggregate) can be initialized with a brace-enclosed initializer-list.

Example:

struct X { int a; char* b; int c; };
X xObj = { 1, "hi there" };

Initialization Order

The general order of initialization is:
  1. Base class objects (if present)
  2. Member data objects
  3. Constructor function code
Both base class objects and member data objects are initialized in declaration order, regardless of the order in the initialization list in the constructor definition.

There's a common sense reason why the constructor code is executed last - the constructor may operate upon member or base object data, which must be already initialized and ready to be modified or accessed.

Destructors are called in the reverse order (which is the main reason why initialization is dependent on declaration order, rather than on the order in the initialization list).

From Stroustrup, The C++ Programming Language, 2nd edition:

Virtual base classes constitute a special case. Virtual bases are constructed before any nonvirtual bases and in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes; 'left-to-right' is the order of appearance of the base class names in the declaration of the derived class.
From Stroustrup, The C++ Programming Language, 3rd edition:
    The constructor of a virtual base is invoked (implicitly or explicitly) from the constructor for the complete object (the constructor for the most derived class).

Different Ways to Construct and Destruct

From Stroustrup, The C++ Programming Language, 3rd edition:
    Consider the different ways an object can be created and how it gets destroyed afterwards. An object can be created as:
    1. A named automatic object, which is created each time its declaration is encountered in the execution of the program and destroyed each time the program exits the block in which it occurs
    2. A free-store object, which is created using the new operator and destroyed using the delete operator
    3. A nonstatic member object, which is created as a member of another class object and created and destroyed when the object of which it is a member is created and destroyed
    4. An array element, which is created and destroyed when the array of which it is an element is created and destroyed
    5. A local static object, which is created the first time its declaration is encountered in the execution of the program and destroyed once at the termination of the program
    6. A global, namespace, or class static object, which is created once "at the start of the program" and destroyed once at the termination of the program
    7. A temporary object, which is created as part of the evaluation of an expression and destroyed at the end of the full expression in which it occurs
    8. An object placed in memory obtained from a user-supplied function guided by arguments supplied in the allocation operation
    9. A union member, which may not have a constructor or a destructor

This page constructed by Cliff Green, Copyright © 2001.