Demystifying Constructors in C++

Constructors are the unsung heroes of C++ classes. Like a maestro coordinating a complex orchestra, constructors enable robust, flexible object creation rooted in sane defaults.

Yet their syntax quirks often mystify newcomers. This friendly guide will explain everything you need to grasp about constructors – from basic to advanced – with clear examples. Soon these special functions will be indispensable allies!

Why Care About Constructors?

Constructors run automatically when new class objects are made to initialize member attributes. This saves tons of repetitive manual assignment code each time.

More crucially, constructors establish proper invariant conditions that the object should always uphold throughout its lifetime. For example:

class BankAccount {

  public:

  double balance;

  BankAccount() {
    balance = 0; // All accounts start 0 balance  
  }

};

Now balance begins zeroed out rather than garbage absent a constructor.

Later functionality can rely on invariants like this to prevent weird edge case bugs. So let‘s start constructing!

Constructor Types

C++ supports several constructor flavors…

Default Constructors

Default constructors take no arguments. They provide a no-fuss way to instantiate objects.

class Person {

  public:

  string name;

  Person() {
    name = "John Doe"; // Default name
  }

};

Person p1; // p1.name = "John Doe"

Without any arguments, Person() provides backup defaults.

Defining a custom default rather than Person() {} handles unspecified cases purposefully.

Parameterized Constructors

Parameterized constructors accept arguments to allow customized initialization.

For example:

class Person {

  public:

  string name;
  int age;

  // Parameterized constructor 
  Person(string initName, int initAge) {

    name = initName;
    age = initAge;

  }

};

Person p1("Mary", 26);

Now we can instantiate Person with varied ages and names!

Arguments passed during object creationtransparently initialize attributes without extra code.

Copy Constructors

Copy constructors create a new object copied from an existing object.

The copy constructor accepts a reference to the source object to duplicate:


class Square {

  public:

  int sideLength;

  // Copy constructor
  Square(const Square &source) {
     sideLength = source.sideLength;     
  }

};

Square s1(5); 

Square s2(s1); // Duplicate s1

Now s2 replicates s1 thanks to the copy constructor rather than needing manual attribute copying.

Copy constructors are paramterized constructors enabling duplication. Under the hood, they:

  1. Allocate memory for new object
  2. Copy source object contents over

This is cheaper than manual clone() functions since attributes get copied during initialization itself.

Later we‘ll see how to optimize copying further.

Constructor Overloading

Like regular functions, C++ supports overloaded constructors – multiple constructors within a class differing by arguments.

For example:

class Rectangle {

  int length;
  int breadth;

  public:

  Rectangle(); // Default constructor

  Rectangle(int l); // One parameter

  Rectangle(int l, int b); // Two parameters

};

int main() {

  Rectangle r1; // Default
  Rectangle r2(5); // One parameter
  Rectangle r3(3,7); // Two parameters  

}

Now a Rectangle can get created in three different ways:

  1. Default no-argument
  2. Customized length
  3. Fully customized dimensions

Constructor overloading enables flexible object creation from a combination of defaults and parameters. The compiler picks the right one based on arguments passed.

Overloading works just like regular function overloading – differing by parameter types and counts.

Next let‘s plug constructors into C++‘s inheritance machinery…

Inheriting Constructors

For derived classes, base class constructors run first during initialization even if not explicitly called:

class Base {

  public: 

  int baseMember;  

  Base() {
    cout << "Base constructor!";
  }

};

class Derived: public Base {

  public:

  int derivedMember;

  Derived() {
    cout << "Derived constructor!";    
  }

};


Derived d; // 1. Base constructor!
            // 2. Derived constructor!

The order enforced ensures base invariants using baseMember initialize before specialized derived behavior can run.

We can customize this order through explicit constructor calls:

Derived() : Base() { // Explicit Base() call
  cout << "Derived constructor"; 
}

So base constructors form a first-class foundation for specialized subclasses.

Now what distinguishes constructors from regular functions?

Constructors vs Functions

While constructors seem like regular member functions, distinctions matter:

ConstructorsFunctions
Class nameCustom name
No returnMust return
Automatic invocationManual invocation
Initialization onlyAny purpose

The specializations make constructors purpose-built for initialization. Functions encapsulate reusable logic across more flexible contexts.

Constructors also link to their counterpart – destructors…

Constructors vs Destructors

While constructors initialize new objects, destructors do cleanup when objects expire.

For example:

class Resource {

  // Acquire resource
  Resource() {
    cout << "Constructed!";
  }

  ~Resource() {
    // Free resource
    cout << "Destructed!";
  }

};


void foo() {

  Resource r; // "Constructed!"

  // ...

  // Destructor called automatically when
  // r goes out of scope 
}

So constructors and destructors bookend an object‘s lifetime.

Now that you see constructors power in action across basic to advanced scenarios, let‘s consolidate those skills with some practice exercises!

Constructor Practice Exercises

Solidify your constructor chops through some hands-on problems:

  1. Define a Student class with a name and id. Create a constructor initializing those members.

  2. Overload the constructor with default parameter values.

  3. Subclass Student into GraduateStudent. Ensure base constructor runs first.

  4. Construct a Professor class from Person. Has publications member.

  5. Implement copy constructors and test copies for both classes.

Check solutions here.

Congratulations – you can now employ constructors like a pro!

Closing Thoughts

This guide explored all things constructors – from fundamentals like default and parameterized types to subtleties around inheritance and copying.

Key Takeaways:

  • Constructors automatically initialize new objects
  • Overloading enables flexible instantiation
  • Order matters when inheriting
  • Copy constructors duplicate objects
  • Destructors handle cleanup

You‘re now equipped to utilize these special functions in building far more intuitive class architectures!

For more C++ mastery, check out these resources:

  1. LearnCpp In-Depth Constructor Guide
  2. C++ Copy Constructor Explanation

Happy constructing!

Did you like those interesting facts?

Click on smiley face to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

      Interesting Facts
      Logo
      Login/Register access is temporary disabled