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:
- Allocate memory for new object
- 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:
- Default no-argument
- Customized length
- 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:
Constructors | Functions |
---|---|
Class name | Custom name |
No return | Must return |
Automatic invocation | Manual invocation |
Initialization only | Any 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:
Define a
Student
class with aname
andid
. Create a constructor initializing those members.Overload the constructor with default parameter values.
Subclass
Student
intoGraduateStudent
. Ensure base constructor runs first.Construct a
Professor
class fromPerson
. Haspublications
member.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:
Happy constructing!