Menu

C++ Constructors: Initialize Objects the Right Way

A constructor is the special member function that runs when an object is created. Learn default, parameterized, and copy constructors, member initializer lists, and how to avoid leaving objects half-initialized.

This page includes runnable editors - edit, run, and see output instantly.

What a Constructor Is

On the previous page you defined classes and gave them member variables. But a freshly created object's members hold whatever garbage was in that memory unless you set them. A constructor fixes that: it's a special member function that runs automatically the moment an object is created, and its one job is to leave the object in a valid, fully-initialized state.

A constructor has the same name as the class and no return type - not even void. You never call it directly; the compiler calls it for you whenever an object comes into existence.

The Counter() with no parameters is called the default constructor - it's the one used when you create an object without passing any arguments.

Parameterized Constructors

A constructor that takes no arguments is fine, but usually you want to create an object with specific values. A parameterized constructor accepts arguments and uses them to initialize the members.

A class can have more than one constructor, as long as their parameter lists differ - this is ordinary function overloading applied to constructors. Here Point can be created with or without coordinates:

A common gotcha: Point p(); does not create an object - the compiler reads it as the declaration of a function named p that returns a Point. To call the default constructor, write Point p; (no parentheses) or Point p{}; with braces.

Member Initializer Lists

So far the examples assigned to members inside the constructor body. That works for simple types, but it's the wrong tool. By the time the body runs, every member has already been default-constructed; the body then throws that away and assigns over it. A member initializer list initializes each member directly, before the body, in one step.

The syntax is a colon after the parameter list, then member(value) pairs:

For a string member this also avoids constructing an empty string and then assigning - the initializer list builds it correctly on the first try.

The initializer list isn't just an optimization; it's mandatory in three cases, because the body can only assign, not initialize:

  • const members - you can't assign to a const after it exists.
  • Reference members - a reference must be bound when it's born.
  • Members whose type has no default constructor.
class Sensor {
    const int id;        // const member
    int& slot;           // reference member

public:
    Sensor(int sensorId, int& s) : id(sensorId), slot(s) {}
    // Trying to set id or slot in the body would not compile.
};

One subtlety to know: members are initialized in the order they are declared in the class, not the order you list them in the initializer list. If one member's initializer reads another, declaration order is what matters - mismatching the two is a classic source of using a not-yet-initialized value.

Default Arguments and Delegating Constructors

You don't always need separate overloads. Default arguments let a single constructor cover several cases - omit an argument and the default fills in:

Be careful combining a defaulted parameterized constructor with a separate Point() default constructor - the compiler can't tell which to call for Point p; and will report an ambiguity. Pick one approach.

When you do have multiple constructors that share setup, a delegating constructor (C++11) lets one constructor call another instead of repeating the logic. You "delegate" by putting the other constructor in the initializer list:

The Copy Constructor

When you create an object as a copy of another - by passing it by value, returning it, or writing Foo b = a; - the copy constructor runs. Its signature takes a const reference to the same type:

ClassName(const ClassName& other);

If you don't write one, the compiler generates a default copy constructor that copies each member. For classes holding only values (ints, strings, vectors), that's exactly right and you should not write your own.

The big gotcha lives in the next chapter on memory: if your class owns a raw pointer to heap memory, the default copy constructor copies the pointer, not the data - so two objects end up pointing at the same memory, and both will try to free it. That's the double-free bug. The rule of thumb is the Rule of Three/Five: if you write a custom destructor, you almost certainly need a custom copy constructor (and copy assignment) too. In modern C++ the cleaner fix is to hold a std::vector or smart pointer so the compiler-generated copy just works.

Note also that taking the parameter by reference is required, not optional: a copy constructor that took its argument by value would need to copy the argument to call itself - infinite recursion that won't even compile.

Next: Destructors

A constructor sets an object up; a destructor tears it down. When an object goes out of scope or is deleted, its destructor runs automatically - the perfect place to release files, network connections, or heap memory the object was holding. The next page covers how destructors work, when exactly they fire, and how they pair with constructors to give C++ its powerful RAII pattern.

Frequently Asked Questions

What is a constructor in C++?

A constructor is a special member function with the same name as the class and no return type. It runs automatically when an object is created, and its job is to bring the object into a valid, fully-initialized state before any other code uses it.

What is the difference between a default and a parameterized constructor?

A default constructor takes no arguments and is used when you create an object without supplying values (Point p;). A parameterized constructor takes arguments so the caller can initialize the object with specific values (Point p(3, 4);). A class can have both because constructors are overloaded by their parameter lists.

Why should I use a member initializer list in C++?

A member initializer list (: name(n), age(a)) initializes members directly, before the constructor body runs. It is required for const members, references, and members without a default constructor, and it avoids the wasteful default-construct-then-assign that happens when you assign inside the body.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED