Constructors

Back

Loading concept...

C++ Constructors: Building Objects Like a Pro!

The Big Picture: What Are Constructors?

Imagine you’re a toy factory robot. Every time someone orders a toy, you need to build it. But here’s the thing—you can’t just make an empty toy box and call it done! You need to:

  1. Set up the toy’s parts (give it eyes, wheels, buttons)
  2. Make sure everything works before shipping

Constructors are like your toy-building instructions. They run automatically whenever you create a new object, making sure it’s ready to use from the very first moment.

class Toy {
public:
    string name;
    int batteries;

    // This is a constructor!
    Toy() {
        name = "Unknown Toy";
        batteries = 0;
    }
};

When you write Toy myToy;, the constructor runs automatically. Your toy is born with a name and battery count!


1. Default Constructor: The “No Questions Asked” Builder

What Is It?

A default constructor takes no arguments. It’s like ordering a pizza without customizations—you get the standard version.

The Magic: Compiler Gives You One Free!

If you don’t write ANY constructor, C++ creates an invisible default constructor for you. It does nothing special—just creates the object.

class Robot {
public:
    int power;
    string color;
    // No constructor written!
    // Compiler creates: Robot() { }
};

Robot r1; // Works! Uses invisible default

But Wait—There’s a Catch!

The moment you write ANY constructor, the free one disappears!

class Robot {
public:
    int power;
    string color;

    // Custom constructor
    Robot(int p) {
        power = p;
    }
};

Robot r1;      // ERROR! No default constructor
Robot r2(100); // Works!

Getting Your Default Back

Want both? Write the default one yourself!

class Robot {
public:
    int power;
    string color;

    // Default constructor
    Robot() {
        power = 50;
        color = "silver";
    }

    // Parameterized constructor
    Robot(int p) {
        power = p;
        color = "silver";
    }
};

Robot r1;      // Works! Uses default
Robot r2(100); // Works! Uses parameterized

2. Parameterized Constructors: Custom Orders Welcome!

What Is It?

A parameterized constructor accepts inputs when you create an object. It’s like ordering a pizza WITH toppings—you tell it exactly what you want!

class Pizza {
public:
    string size;
    int toppings;

    Pizza(string s, int t) {
        size = s;
        toppings = t;
    }
};

Pizza myPizza("large", 3);
// size = "large", toppings = 3

Real Example: A Game Character

class Hero {
public:
    string name;
    int health;
    int attack;

    Hero(string n, int h, int a) {
        name = n;
        health = h;
        attack = a;
    }
};

Hero warrior("Aria", 100, 25);
Hero mage("Zephyr", 60, 40);

Each hero starts with exactly what you specify!

Multiple Constructors = Flexibility

class Account {
public:
    string owner;
    double balance;

    // Full details
    Account(string o, double b) {
        owner = o;
        balance = b;
    }

    // Just owner, start with $0
    Account(string o) {
        owner = o;
        balance = 0.0;
    }
};

Account rich("Alice", 10000);
Account newbie("Bob"); // balance = 0

3. Initialization Lists: The Fast Lane

The Problem with Assignment

When you do name = n; inside a constructor, two things happen:

  1. The variable is created (with garbage or default value)
  2. Then it’s assigned your value

That’s two steps when you only need one!

Initialization Lists: One Step Wonder

class Player {
public:
    string name;
    int score;

    // Using initialization list
    Player(string n, int s)
        : name(n), score(s)  // <- This is the magic!
    {
        // Constructor body (can be empty)
    }
};

The : name(n), score(s) syntax initializes directly—no wasted steps!

When You MUST Use Initialization Lists

Some things can’t be assigned later. You MUST initialize them at birth:

1. const Members

class Circle {
    const double PI;  // Can't change after creation
public:
    Circle() : PI(3.14159) { }
    // PI = 3.14159; // ERROR! Can't assign const
};

2. Reference Members

class Wrapper {
    int& ref;  // References must be bound at creation
public:
    Wrapper(int& r) : ref(r) { }
};

3. Members Without Default Constructors

class Engine {
public:
    Engine(int hp) { /* needs parameter */ }
};

class Car {
    Engine engine;
public:
    Car() : engine(200) { }  // Must init Engine!
};

The Order Matters!

Initialization happens in the order members are declared, not listed:

class Demo {
    int a;  // Declared first
    int b;  // Declared second
public:
    Demo() : b(10), a(5) { }
    // a is initialized first (to 5)
    // b is initialized second (to 10)
};

4. Delegating Constructors: Don’t Repeat Yourself!

The Problem: Code Duplication

class Rectangle {
    int width, height;
    string color;
public:
    Rectangle() {
        width = 10;
        height = 10;
        color = "white";
    }

    Rectangle(int w, int h) {
        width = w;
        height = h;
        color = "white";  // Repeated!
    }

    Rectangle(int w, int h, string c) {
        width = w;
        height = h;
        color = c;
    }
};

See how color = "white" and similar logic repeats? Messy!

The Solution: Delegation

One constructor can call another constructor to do the heavy lifting:

class Rectangle {
    int width, height;
    string color;
public:
    // Main constructor (does all the work)
    Rectangle(int w, int h, string c)
        : width(w), height(h), color(c)
    { }

    // Delegates to main constructor
    Rectangle(int w, int h)
        : Rectangle(w, h, "white")
    { }

    // Delegates with defaults
    Rectangle()
        : Rectangle(10, 10, "white")
    { }
};

Now there’s ONE place where initialization happens. Change it once, fixed everywhere!

The Flow

Rectangle()
    → calls Rectangle(10, 10, "white")
        → initializes width=10, height=10, color="white"
graph TD A["Rectangle&#35;40;&#35;41;"] -->|delegates to| B["Rectangle&#35;40;10, 10, &&#35;39;white&&#35;39;&#35;41;"] C["Rectangle&#35;40;w, h&#35;41;"] -->|delegates to| D["Rectangle&#35;40;w, h, &&#35;39;white&&#35;39;&#35;41;"] B --> E["Main Constructor"] D --> E F["Rectangle&#35;40;w, h, c&#35;41;"] --> E

Rules for Delegation

  • Delegation must be in the initialization list
  • You can only delegate to one constructor
  • No other initializations allowed when delegating
// WRONG!
Rectangle() : Rectangle(10, 10), color("red") { }
// Can't mix delegation with other initializers

5. The explicit Keyword: No Sneaky Conversions!

The Problem: Implicit Conversion

C++ sometimes converts types automatically. This can cause surprises:

class Box {
    int size;
public:
    Box(int s) : size(s) { }
};

void ship(Box b) {
    // ships the box
}

ship(42);  // Works! 42 becomes Box(42)

Wait—we passed a number 42 but the function wanted a Box! C++ secretly created Box(42) for us.

Sometimes this is convenient. Sometimes it’s dangerous:

Box myBox = 100;     // Looks like a number!
myBox = 200;         // Replaces box with Box(200)

This hides what’s really happening. Bugs love to hide here!

The Solution: explicit

Add explicit to stop automatic conversions:

class Box {
    int size;
public:
    explicit Box(int s) : size(s) { }
};

void ship(Box b) { }

ship(42);          // ERROR! No implicit conversion
ship(Box(42));     // OK! Explicit creation

Box b1(10);        // OK! Direct initialization
Box b2 = 10;       // ERROR! Implicit conversion blocked
Box b3 = Box(10);  // OK! Explicit

When to Use explicit

Use it when:

  • The constructor could be called with a single argument
  • Implicit conversion would be confusing or error-prone

Skip it when:

  • You WANT the conversion (like string from const char*)

Real-World Example

class Kilometer {
    double value;
public:
    explicit Kilometer(double v) : value(v) { }
};

class Mile {
    double value;
public:
    explicit Mile(double v) : value(v) { }
};

void drive(Kilometer dist) { }

drive(5.0);           // ERROR! Good—unclear unit
drive(Kilometer(5));  // OK! Clear intent

Without explicit, you might accidentally mix miles and kilometers. With it, you must be clear about what you mean!


Quick Summary

Constructor Type Purpose Example
Default No parameters needed Robot() { }
Parameterized Accepts custom values Robot(int power)
Init List Direct initialization : name(n), age(a)
Delegating Call another constructor : OtherConstructor()
explicit Block implicit conversion explicit Box(int)

The Constructor Journey

graph TD A["Create Object"] --> B{Any Constructor?} B -->|No| C["Compiler Default"] B -->|Yes| D{Which Type?} D --> E["Default - No Args"] D --> F["Parameterized - With Args"] E --> G{Uses Init List?} F --> G G -->|Yes| H["Direct Initialization"] G -->|No| I["Assignment in Body"] H --> J{Delegates?} I --> J J -->|Yes| K["Call Other Constructor"] J -->|No| L["Object Ready!"] K --> L

Remember This!

  1. Default constructor = builds with zero inputs
  2. Parameterized constructor = builds with your inputs
  3. Initialization list = faster, required for const/refs
  4. Delegating constructors = reuse logic, stay DRY
  5. explicit = say NO to sneaky conversions

Constructors are your object’s birth story. Master them, and you control exactly how every object comes to life!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.