Intermediate · Classes, constructors and RAII · topic 14 of 42 · 30 min
Constructors and destructors
Explanation
Why constructors?
An object should be valid from the very first moment: a Student without a roll number, or a Ticket with a negative fare, should never exist. A constructor is a member function with the same name as the class and no return type. C++ calls it automatically every time an object is created.
#include <iostream>
#include <stdexcept>
#include <string>
class Ticket {
public:
Ticket(std::string passenger, int farePaise)
: passenger_(std::move(passenger)), farePaise_(farePaise) { // member initialiser list
if (farePaise_ < 0) throw std::invalid_argument("negative fare");
}
void print() const { std::cout << passenger_ << ": " << farePaise_ / 100 << '\n'; }
private:
std::string passenger_;
int farePaise_;
};
int main() {
Ticket t("Asha", 145050);
t.print(); // Asha: 1450
// Ticket bad; // error: no default constructor
}The member initialiser list
Members are always initialised BEFORE the constructor body runs. If you assign them inside the body, each member is first default-constructed and then overwritten, which is wasteful. Some members can only be set in the initialiser list: const members, reference members, and members whose type has no default constructor.
Default constructor and default member values
class Counter {
public:
Counter() = default; // use the default member values below
explicit Counter(int start) : count_(start) {}
int value() const { return count_; }
private:
int count_ = 0; // default member initialiser (C++11)
};
Counter a; // 0
Counter b(10); // 10
Counter c{5}; // 5, brace initialisationexplicit
A constructor that takes one argument can be used by the compiler to convert silently: void show(Counter c); show(42); would build a Counter from 42 behind your back. Marking the constructor explicit forbids those silent conversions. Make single-argument constructors explicit unless you really want the conversion.
Destructors
#include <iostream>
class Lamp {
public:
explicit Lamp(int id) : id_(id) { std::cout << "Lamp " << id_ << " on\n"; }
~Lamp() { std::cout << "Lamp " << id_ << " off\n"; }
private:
int id_;
};
int main() {
Lamp a(1);
{
Lamp b(2);
} // b is destroyed here
std::cout << "end of main\n";
} // a is destroyed here
// Output:
// Lamp 1 on
// Lamp 2 on
// Lamp 2 off
// end of main
// Lamp 1 offA destructor has no parameters and no return type, and a class has exactly one. You almost never call it yourself; C++ calls it at the end of the object's lifetime (end of scope for local objects, delete for heap objects, or when the containing object or container is destroyed).
Common mistakes
- Writing Ticket t(); to create an object with no arguments. That declares a FUNCTION named t (the "most vexing parse"). Write Ticket t; or Ticket t{};.
- Forgetting to initialise a member such as an int, leaving garbage. Give every member a default value.
- Doing heavy work that can fail silently in a constructor. If construction cannot produce a valid object, throw an exception.
- Throwing exceptions out of a destructor. Destructors are noexcept by default; a throw from one usually ends the program.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Constructors and destructors · 1/6
A constructor sets up a new object so it is valid from its very first moment.
Prefer a video by a teacher? Find free videos on “c++ constructors destructors member initializer list explicit” on YouTube ↗
In real life: A Student record that is always valid
A school management system creates a Student object for each admission. The constructor demands a name and a roll number and rejects an empty name, so the rest of the system never has to check whether a Student is half-filled.
#include <iostream>
#include <stdexcept>
#include <string>
class Student {
public:
Student(std::string name, int roll) : name_(std::move(name)), roll_(roll) {
if (name_.empty()) throw std::invalid_argument("name required");
}
const std::string& name() const { return name_; }
int roll() const { return roll_; }
private:
std::string name_;
int roll_;
};
int main() {
Student s("Meena", 17);
std::cout << s.roll() << ' ' << s.name() << '\n'; // 17 Meena
}Practice (0/4 done)
Which is a correct constructor for class Box?
What is the output? struct A { A() { std::cout << "a"; } ~A() { std::cout << "x"; } }; int main() { A one; A two; }
class P { const int id_; public: P(int id) { id_ = id; } }; Why does this fail to compile?
Write a class Timer that prints "start" in its constructor with a label, and in its destructor prints the label and how many milliseconds passed. Use <chrono> (std::chrono::steady_clock). Create a Timer at the start of a block that runs a loop, and see the time printed when the block ends.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- RAII: let objects clean up for youClasses, constructors and RAII · same module
- Copying objects: the rule of three, five and zeroClasses, constructors and RAII · same module
- Constructors and thisJava Programming (Core to Spring Boot) · Classes and objects
- Classes, objects and constructorsC# and .NET · Classes and objects
- Virtual functions and polymorphismOperator overloading, inheritance and polymorphism
- StructsC Programming (from zero to systems) · Structs, unions, enums and files
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
When does a local object's destructor run?
Answer: When the object goes out of scope
Local objects are destroyed automatically at the end of their scope.
You wrote Student(std::string name, int roll). What happens with Student s;?
Answer: Compile error: no default constructor exists
Writing any constructor stops the compiler from generating the default one.
Why mark a one-argument constructor explicit?
Answer: To stop silent implicit conversions from that argument type
explicit prevents surprises such as passing 42 where a Counter is expected.
In what order are members initialised?
Answer: The order they are declared in the class
Declaration order always wins; keep the initialiser list in the same order.
What does Widget w(); declare?
Answer: A function named w that returns a Widget
This is the "most vexing parse". Use Widget w; or Widget w{};.
Which pair makes a deep copy automatically when you copy the object?Related
Answer: A std::vector member
std::vector copies all its elements into new memory.
When does Java add a default no-argument constructor?Related
Answer: Only when the class declares no constructor at all
Writing any constructor removes the default one.
An exception is thrown inside a function. What happens to the local objects already created there?Related
Answer: Their destructors run as the exception leaves the scope (stack unwinding)
Stack unwinding destroys local objects in reverse order, which is what makes RAII reliable.
What does : this(name, 0m) after a constructor header do?Related
Answer: Calls another constructor of the same class first
It chains to another constructor to reuse its setup code.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Classes, constructors and RAII” before its exam: RAII: let objects clean up for you, Copying objects: the rule of three, five and zero.
- Module 4 exam: Classes, constructors and RAII · 12 questions · 20 min, without help. 4 topics of this module still to finish first.
- Then the next module, Operator overloading, inheritance and polymorphism. 42 topics left in the course before the final exam.