C++ Constructor Overloading
Introduction to Constructor Overloading
Constructor overloading means having multiple constructors in the same class with different parameter lists. This allows objects to be created in different ways depending on the provided arguments.
Overloading provides flexibility in initializing objects with varying amounts of data.
Example of Constructor Overloading
Here’s a class with multiple constructors for different scenarios. It uses delegating constructors and initializer lists to avoid duplicate initialization logic, and marks the single-parameter constructor `explicit` to prevent unintended implicit conversions.
#include <iostream>
using namespace std;
class Rectangle {
private:
int width;
int height;
public:
// Default constructor delegates to the 2-arg constructor
Rectangle() : Rectangle(0, 0) {}
// Single-parameter (square) constructor — make it explicit to avoid implicit conversions
explicit Rectangle(int w) : Rectangle(w, w) {}
// Two-parameter constructor — primary initialization point
Rectangle(int w, int h) : width(w), height(h) {}
int area() const { return width * height; }
};
int main() {
Rectangle r1; // Calls default constructor
Rectangle r2(5); // Calls single-parameter constructor
Rectangle r3(4, 6); // Calls two-parameter constructor
cout << "r1 area: " << r1.area() << '\n';
cout << "r2 area: " << r2.area() << '\n';
cout << "r3 area: " << r3.area() << '\n';
return 0;
}
r1 area: 0 r2 area: 25 r3 area: 24
Using Initializer Lists
Initializer lists directly initialize members and are required for `const` data members and references. They are also typically more efficient than assignment in the constructor body.
Important: actual initialization order is the order of member declaration in the class, not the order listed in the initializer list.
class Sample {
const int id;
int& ref;
int a;
int b;
public:
Sample(int i, int& r)
: id(i), ref(r), b(2), a(1) {} // Initializes in declaration order: id, ref, a, b
};
Delegating Constructors (C++11+)
Use one constructor as the single point of initialization and have other constructors delegate to it. This removes duplicated setup logic and keeps invariants centralized.
class Vec2 {
int x, y;
public:
Vec2() : Vec2(0, 0) {}
explicit Vec2(int xy) : Vec2(xy, xy) {}
Vec2(int x_, int y_) : x(x_), y(y_) {}
};
Default Arguments vs Overloads
Default arguments can reduce the number of overloads, but be cautious: they can create ambiguities with other constructors.
class C {
public:
explicit C(int); // #1
C(int, int = 0); // #2 (has a default)
};
// C c(5); // ❌ Ambiguous between #1 and #2
Best Practices
- Prefer initializer lists over assignment in the constructor body.
- Use delegating constructors to centralize initialization and maintain invariants.
- Mark single-parameter constructors `explicit` to prevent unintended implicit conversions.
- Keep overload sets small and coherent; consider default parameters where they won’t cause ambiguity.
- Make accessor-like methods `const` (e.g., `int area() const`).
- Keep data members private and expose behavior through member functions.