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.
[ Top | Bottom | Previous section | Next section ]
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)
// ...
}
[ Top | Bottom | Previous section | Next section ]
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.
[ Top | Bottom | Previous section | Next section ]
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
// ...
};
[ Top | Bottom | Previous section | Next section ]
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).
// ...
}
[ Top | Bottom | Previous section | Next section ]
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.
[ Top | Bottom | Previous section | Next section ]
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;
[ Top | Bottom | Previous section | Next section ]
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).
[ Top | Bottom | Previous section | Next section ]