[10] Constructors
(Part of C++ FAQ Lite, Copyright © 1991-96, Marshall Cline, cline@parashift.com)


FAQs in section [10]:


[10.1] What's the deal with constructors?

Constructors build objects from dust.

Constructors are like "init functions". They turn a pile of arbitrary bits into a living object. Minimally they initialize internally used fields. They may also allocate resources (memory, files, semaphores, sockets, etc).

"ctor" is a typical abbreviation for constructor.

TopBottomPrevious sectionNext section ]


[10.2] Is there any difference between List x; and List x();?

A big difference!

Suppose that List is the name of some class. Then function f() declares a local List object called x:

    void f()
    {
      List x;     // Local object named x (of class List)
      
// ...
    }

But function g() declares a function called x() that returns a List:

    void g()
    {
      List x();   
// Function named x (that returns a List)
      
// ...
    }

TopBottomPrevious sectionNext section ]


[10.3] How can I make a constructor call another constructor as a primitive?

No way.

Dragons be here: if you call another constructor, the compiler initializes a temporary local object; it does not initialize this object. You can combine both constructors by using a default parameter, or you can share their common code in a private init() member function.

TopBottomPrevious sectionNext section ]


[10.4] Is the default constructor for Fred always Fred::Fred()?

No. A "default constructor" is a constructor that can be called with no arguments. Thus a constructor that takes no arguments is certainly a default constructor:

    class Fred {
    public:
      Fred();   
// Default constructor: can be called with no args
      
// ...
    };

However it is possible (and even likely) that a default constructor can take arguments, provided they are given default values:

    class Fred {
    public:
      Fred(int i=3, int j=5);   
// Default constructor: can be called with no args
      
// ...
    };

TopBottomPrevious sectionNext section ]


[10.5] Which constructor gets called when I create an array of Fred objects?

Fred's default constructor.

There is no way to tell the compiler to call a different constructor. If your class Fred doesn't have a default constructor, attempting to create an array of Fred objects is trapped as an error at compile time.

    class Fred {
    public:
      Fred(int i, int j);
      
// ... assume there is no default constructor in class Fred ...
    };
    
    main()
    {
      Fred a[10];               
// ERROR: Fred doesn't have a default constructor
      Fred* p = new Fred[10];   
// ERROR: Fred doesn't have a default constructor
    }

However if you are creating an STL vector<Fred> rather than an array of Fred (which you probably should be doing anyway since arrays are evil), you don't have to have a default constructor in class Fred, since you can give the vector a Fred object to be used to initialize the elements:

    #include <vector>
    using namespace std;
    
    main()
    {
      vector<Fred> a(10, Fred(5,7));
      
// The 10 Fred objects in vector a will be initialized with Fred(5,7).
      
// ...
    }

TopBottomPrevious sectionNext section ]


[10.6] What is the "Named Constructor Idiom"?

A technique that provides more intuitive and/or safer construction operations for users of your class.

The problem is that constructors always have the same name as the class. Therefore the only way to differentiate between the various constructors of a class is by the parameter list. But if there are lots of constructors, the differences between the constructors becomes somewhat subtle and error prone.

With the Named Constructor Idiom, you declare all the class's constructors in the private: or protected: sections, and you provide public static methods that return an object. These static methods are the so-called "Named Constructors." In general there is one such static method for each different way to construct an object.

For example, suppose we are building a Point class that represents a position on the X-Y plane. Turns out there are two common ways to specify a 2-space coordinate: rectangular coordinates (X+Y), polar coordinates (Radius+Angle). (Don't worry if you can't remember these; the point isn't the particulars of coordinate systems; the point is that there are several ways to create a Point object). Unfortunately the parameters for these two coordinate systems are the same: two floats. This would create an ambiguity error in the overloaded constructors:

    class Point {
    public:
      Point(float x, float y);     
// Rectangular coordinates
      Point(float r, float a);     
// Polar coordinates (radius and angle)
      
// ERROR: Overload is Ambiguous: Point::Point(float,float)
    };
    
    main()
    {
      Point p = Point(5.7, 1.2);   
// Ambiguous: Which coordinate system?
    }

One way to solve this ambiguity is to use the Named Constructor Idiom:

    #include <math.h>              // To get sin() and cos()
    
    class Point {
    public:
      static Point rectangular(float x, float y);      
// Rectangular coord's
      static Point polar(float radius, float angle);   
// Polar coordinates
      
// These static methods are the so-called "named constructors"
      
// ...
    private:
      Point(float x, float y);     
// Rectangular coordinates
      float x_, y_;
    };
    
    inline Point::Point(float x, float y)
    : x_(x), y_(y) { }
    
    inline Point Point::rectangular(float x, float y)
    { return Point(x, y); }
    
    inline Point Point::polar(float radius, float angle)
    { return Point(radius*cos(angle), radius*sin(angle)); }

Now the users of Point have a clear and unambiguous syntax for creating Points in either coordinate system:

    main()
    {
      Point p1 = Point::rectangular(5.7, 1.2);   
// Obviously rectangular
      Point p2 = Point::polar(5.7, 1.2);         
// Obviously polar
    }

Make sure your constructors are in the protected: section if you expect Fred to have derived classes.

The Named Constructor Idiom can also be used to make sure your objects are always created via new.

TopBottomPrevious sectionNext section ]


[10.7] Why can't I initialize my static member data in my constructor's initialization list?

Because you must explicitly define your class's static data members.

Fred.h:

    class Fred {
    public:
      Fred();
      
// ...
    private:
      int i_;
      static int j_;
    };

Fred.cpp (or Fred.C or whatever):

    Fred::Fred()
      : i_(10)  
// OK: you can (and should) initialize member data this way
        j_(42)  
// Error: you cannot initialize static member data like this
    {
      
// ...
    }
    
    
// You must define static data members this way:
    int Fred::j_ = 42;

TopBottomPrevious sectionNext section ]


[10.8] Why are classes with static data members getting linker errors?

Because static data members must be explicitly defined in exactly one compilation unit. If you didn't do this, you'll probably get an "undefined external" linker error. For example:

    // Fred.h
    
    class Fred {
    public:
      
// ...
    private:
      static int j_;   
// Declares static data member Fred::j_
      
// ...
    };

The linker will holler at you ("Fred::j_ is not defined") unless you define (as opposed to merely declare) Fred::j_ in (exactly) one of your source files:

    // Fred.cpp
    
    #include "Fred.h"
    
    int Fred::j_ = some_expression_evaluating_to_an_int;
    
    
// Alternatively, if you wish to use the implicit 0 value for static ints:
    
// int Fred::j_;

The usual place to define static data members of class Fred is file Fred.cpp (or Fred.C or whatever source file extension you use).

TopBottomPrevious sectionNext section ]


[10.9] What's the "static initialization order fiasco"? NEW!

[Recently created (on 9/97). Click here to go to the next FAQ in the "chain" of recent changes.]

A subtle way to kill your project.

The static initialization order fiasco is a very subtle and commonly misunderstood aspect of C++. Unfortunately it's very hard to detect -- the errors occur before main() begins.

In short, suppose you have two static objects x and y which exist in separate source files, say x.cpp and y.cpp. Suppose further that the constructor for the y object calls some method on the x object.

That's it. It's that simple.

The tragedy is that you have a 50%-50% chance of dying. If the compilation unit for x.cpp happens to get initialized first, all is well. But if the compilation unit for y.cpp get initialized first, then y's constructor will get run before x's constructor, and you're toast. I.e., y's constructor will call a method on the x object, yet the x object hasn't yet been constructed.

I hear they're hiring down at McDonalds. Enjoy your new job flipping burgers.

If think it's "exciting" to play Russian Roulette with live rounds in half the chambers, you can stop reading here. On the other hand if you like to reduce your chances of survival by preventing disasters in a systematic way, you probably want to read the next FAQ.

Note: The static initialization order fiasco does not apply to builtin/intrinsic types like int or char*. For example if you create a static float object, there is never a problem with static initialization order. The only time the static initialization order is truly a fiasco is when your static or global objects have a constructor.

TopBottomPrevious sectionNext section ]


[10.10] How do I prevent the "static initialization order fiasco"? NEW!

[Recently created (on 9/97). Click here to go to the next FAQ in the "chain" of recent changes.]

Use the "construct on first use" idiom, which simply means to wrap your static object inside a function.

For example, suppose you have two classes, Fred and Barney. There is a global Fred object called x, and a global Barney object called y. Barney's constructor invokes the goBowling() method on the x object. The file x.cpp defines the x object:

    // File x.cpp
    #include "Fred.hpp"
    Fred x;

The file y.cpp defines the y object:

    // File y.cpp
    #include "Barney.hpp"
    Barney y;

For completeness the Barney constructor might look something like this:

    // File Barney.cpp
    #include "Barney.hpp"
    
    Barney::Barney()
    {
      
// ...
      x.goBowling();
      
// ...
    }

As described above, the disaster occurs if y is constructed before x, which happens 50% of the time since they're in different source files.

There are many solutions to this problem, but a very simple and completely portable solution is to replace the global Fred object, x, with a global function, x(), that returns the Fred object by reference.

    // File x.cpp
    
    #include "Fred.hpp"
    
    Fred& x()
    {
      static Fred* ans = new Fred();
      return *ans;
    }

Since static local objects are constructed the first time control flows over their declaration (only), the above new Fred() statement will only happen once: the first time x() is called. Every subsequent call will return the same Fred object (the one pointed to by ans). Then all you do is change your usages of x to x():

    // File Barney.cpp
    #include "Barney.hpp"
    
    Barney::Barney()
    {
      
// ...
      x().goBowling();
      
// ...
    }

This is called the Construct On First Use Idiom because it does just that: the global Fred object is constructed on its first use.

The downside of this approach is that the Fred object is never destructed. The C++ FAQ Book has a second technique that answers this concern (but at the cost of opening a "static de-initialization order fiasco").

Note: You don't have to do this for builtin/intrinsic types like int or char*. For example if you create a static or global float object, there is no need to wrap it within a function. The only time the static initialization order is truly a fiasco is when your static or global objects have a constructor.

TopBottomPrevious sectionNext section ]


[10.11] How do I prevent the "static initialization order fiasco" for my static data members? NEW!

[Recently created (on 9/97). Click here to go to the next FAQ in the "chain" of recent changes.]

Just use the same technique just described, but this time use a static member function rather than a global function.

Suppose you have a class X that has a static Fred object:

    // File X.hpp
    
    class X {
    public:
      
// ...
    
    private:
      static Fred x_;
    };

Naturally this static member is initialized separately:

    // File X.cpp
    
    #include "X.hpp"
    
    Fred X::x_;

Naturally also the Fred object will be used in one or more of X's methods:

    void X::someMethod()
    {
      x_.goBowling();
    }

But now the "disaster scenario" is if someone somewhere somehow calls this method before the Fred object gets constructed. For example, if someone else creates a static X object and invokes its someMethod() method during static initialization, then you're at the mercy of the compiler as to whether the compiler will construct X::x_ before or after the someMethod() is called. (Note that the ANSI/ISO C++ committee is working on this problem, but compilers aren't yet generally available that handle these changes; watch this space for an update in the future.)

In any event, it's always portable and safe to change the X::x_ static data member into a static member function:

    // File X.hpp
    
    class X {
    public:
      
// ...
    
    private:
      static Fred& x();
    };

Naturally this static member is initialized separately:

    // File X.cpp
    
    #include "X.hpp"
    
    Fred& X::x()
    {
      static Fred* ans = new Fred();
      return *ans;
    }

Then you simply change any usages of x_ to x():

    void X::someMethod()
    {
      x().goBowling();
    }

If you're super performance sensitive and you're concerned about the overhead of an extra function call on each invocation of X::someMethod() you can set up a static Fred& instead. As you recall, static local are only initialized once (the first time control flows over their declaration), so this will call X::x() only once: the first time X::someMethod() is called:

    void X::someMethod()
    {
      static Fred& x = X::x();
      x.goBowling();
    }

Note: You don't have to do this for builtin/intrinsic types like int or char*. For example if you create a static or global float object, there is no need to wrap it within a function. The only time the static initialization order is truly a fiasco is when your static or global objects have a constructor.

TopBottomPrevious sectionNext section ]


 E-mail the author
C++ FAQ LiteTable of contentsSubject indexAbout the author©Download your own copy ]
Revised Sep 8, 1997