C++ Tutorial

C++ Destructors

A destructor runs when an object dies. Use it to release what the constructor acquired.

The matching end of a constructor

A constructor runs when an object is created. A destructor runs when that object is destroyed. Together they bracket the object's life. If the constructor acquired something — a file, a lock, a log line that must be closed — the destructor is where you release it.

The destructor's name is a tilde plus the class name: ~Session. It takes no parameters and has no return type. You do not call it. The compiler calls it when the object goes out of scope, when a block ends, or when the program leaves main.

Watch the order print

This class does not manage a heap pointer. It only prints, so you can see the calls. The constructor writesstart. The destructor writes end. Create one object in main and read the three lines in order: start, the message from main, then end.

Example

#include <iostream>
#include <string>
using namespace std;

class Session {
  string label;
public:
  Session(string label_in) : label(label_in) {
    cout << "start " << label << endl;
  }
  ~Session() {
    cout << "end " << label << endl;
  }
};

int main() {
  Session a("alpha");
  cout << "inside main" << endl;
  return 0;
}

Compile this in /cpp/try. end alpha appears after inside mainbecause a is destroyed when main returns.

Last constructed, first destroyed

Automatic objects in one block are destroyed in reverse order of construction. Create alpha thenbeta. Destruction prints end beta then end alpha. Think of a stack: the last plate you put down is the first you pick up.

Example

#include <iostream>
#include <string>
using namespace std;

class Session {
  string label;
public:
  Session(string label_in) : label(label_in) {
    cout << "start " << label << endl;
  }
  ~Session() {
    cout << "end " << label << endl;
  }
};

int main() {
  Session a("alpha");
  Session b("beta");
  cout << "inside main" << endl;
  return 0;
}

Expected output, in this order: start alpha, start beta, inside main, end beta, end alpha. If your output differs, you are not looking at automatic objects in one scope, or a print is buffered oddly. On StudyGrid this order is the one you should see.

A block ends the inner object

Extra braces create a nested scope. The object declared inside is destroyed at the closing brace, not at the end of main. That is how you limit a lifetime: put the object in the smallest block that still needs it.

Example

#include <iostream>
#include <string>
using namespace std;

class Session {
  string label;
public:
  Session(string label_in) : label(label_in) {
    cout << "start " << label << endl;
  }
  ~Session() {
    cout << "end " << label << endl;
  }
};

int main() {
  cout << "before" << endl;
  {
    Session inner("inner");
    cout << "inside block" << endl;
  }
  cout << "after" << endl;
  return 0;
}

You should see end inner before after. The destructor ran at the inner closing brace. Nothing was leaked: label is a string member, and that string cleans up its own storage when Session is destroyed.

Release what you acquired

The printouts are a stand-in for real work. If a constructor opens a file, the destructor closes it. If a constructor starts a timer log, the destructor writes the stop line. Pair them. The class then owns the resource for as long as the object lives, and no longer.

Prefer members that clean themselves — string, vector, file streams — over a rawnew in the constructor. Then your destructor might only log, or it might be empty. An empty destructor is still valid; if every member is already safe, you often omit writing one and let the compiler generate it.

Do not new in the constructor unless you delete in the destructor, and even then a container or a smart pointer is the better default. This chapter does not allocate with new. The logger class owns a string and nothing else.

Rules of the destructor

RuleDetail
Name~ClassName with no return type.
ParametersNone. You cannot overload it.
Call siteThe compiler calls it. You do not write a.~Session().
WhenEnd of scope, end of block, or when a container drops the element.

If you write any of the special members (destructor, copy constructor, copy assignment) you take on more lifetime rules. For this tutorial, write a destructor when you have something to undo, and keep the class small. Skip copying these logger objects; one object per scope is enough to see the order.

Lifetime is a stack of objects

Construct in an order you can explain. Destroy in the reverse of that order. Put cleanup in the destructor so every exit from the scope — including an early return — still runs it. Next: operator overloading, which is how a type you wrote can use + or << like an int.

Worked examples

The short listings above are there so you can see the grammar. The programs here use the same statements on quantities that already have units: a speed, a pH, a count of bases. They are classroom numbers. Air resistance is ignored. g is 9.81 m/s² unless a line says otherwise.

Open them in the C++ editor at /cpp/try. Change one measurement and check whether the result still has the right unit.

Engineering

A logger that closes itself

RAII: the constructor acquires, the destructor releases. When Guard leaves main, ~Guard prints release even if you forget a close() call. That is why fstream closes the file when the object dies.

Example

#include <iostream>
using namespace std;

class Guard {
public:
  Guard() { cout << "acquire" << endl; }
  ~Guard() { cout << "release" << endl; }
};

int main() {
  Guard g;
  cout << "log a 12 mT reading" << endl;
  return 0;
}

Physics

A heap sample, then delete

new Sample must meet delete. The destructor runs as part of delete. Printing the mass in ~Sample is a teaching trick so you can see the lifetime; production destructors usually only free resources.

Example

#include <iostream>
using namespace std;

class Sample {
  double grams;
public:
  Sample(double g) : grams(g) {}
  ~Sample() { cout << "done with " << grams << " g" << endl; }
};

int main() {
  Sample *s = new Sample(0.250);
  delete s;
  return 0;
}

FAQ: C++ Destructors

Common questions about this page.

What is the StudyGrid C++ tutorial?

The StudyGrid C++ tutorial is a full beginner-to-advanced track: syntax, types, input, loops, functions, classes, the STL, templates, maps, and lambdas. Each chapter has copy-and-run examples.

Should I run c++ destructors examples locally for better learning?

Yes. Use the browser editor on StudyGrid for a quick check, then Download the example and run it on your computer. Local runs show real errors and the real toolchain, which is one of the fastest ways to learn c++ destructors in this C++ C++ lesson (C++ Destructors).

Is the C++ editor the same as Try Python or Try HTML?

No. Try C++ compiles with g++ at /cpp/try and shows stdout plus compiler messages. Try Python stays at /try. Try HTML stays at /html/try. C++ lessons never open those editors.

Do I need to install a compiler to learn C++?

No. Open a chapter, click Try it in C++, and compile in the browser. You can also download a .cpp file and compile locally with g++.

Where should I start the C++ tutorial?

Start at C++ Intro, then Get Started and Syntax. After the first program, continue to output, variables, and if-else. After classes, open C++ Examples, then templates, map, and lambdas. Use Next at the bottom of each chapter.

Is the C++ tutorial free?

Yes. The C++ workshop on StudyGrid (studygrid.in) is free: dashboard, chapters, and the compile-and-run editor.