Programming Language & Its Applications β Features of Object-Oriented Programming, NEC licence examination syllabus (Nepal Engineering Council).
Base first, derived second, destroyed in exact reverse β and one missing virtual that leaks memory.
virtual-destructor bug in this topic is not theoretical. It is one of the most common real memory leaks in C++ codebases, it produces no warning by default, and the program appears to work perfectly while quietly leaking on every object. Every Qt class inherits a virtual destructor from QObject; every polymorphic base in the standard library has one; std::unique_ptr<Base> holding a Derived depends on it entirely. Search "why base class destructor should be virtual" β and then compile the example below with -fsanitize=address to watch the tool print the exact bytes leaked.#include <iostream>
#include <string>
class Member {
std::string tag;
public:
Member(std::string t) : tag(std::move(t)) {
std::cout << " +member " << tag << "\n";
}
~Member() { std::cout << " -member " << tag << "\n"; }
};
class Base {
Member bm{"base-member"};
public:
Base() { std::cout << " +Base body\n"; }
~Base() { std::cout << " -Base body\n"; }
};
class Middle : public Base {
Member mm{"middle-member"};
public:
Middle() { std::cout << " +Middle body\n"; }
~Middle() { std::cout << " -Middle body\n"; }
};
class Derived : public Middle {
Member d1{"derived-1"};
Member d2{"derived-2"};
public:
Derived() { std::cout << "+Derived body\n"; }
~Derived() { std::cout << "-Derived body\n"; }
};
int main() {
std::cout << "constructing:\n";
{ Derived d;
std::cout << "--- object alive ---\ndestroying:\n"; }
return 0;
}
Output:
constructing:
+member base-member
+Base body
+member middle-member
+Middle body
+member derived-1
+member derived-2
+Derived body
--- object alive ---
destroying:
-Derived body
-member derived-2
-member derived-1
-Middle body
-member middle-member
-Base body
-member base-member
#include <iostream>
/* BROKEN: non-virtual destructor in a polymorphic base */
class BadBase {
public:
BadBase() { std::cout << " +BadBase\n"; }
~BadBase() { std::cout << " -BadBase\n"; }
};
class BadDerived : public BadBase {
int *buffer;
public:
BadDerived() : buffer(new int[1000]) {
std::cout << " +BadDerived (4000 bytes)\n";
}
~BadDerived() {
delete[] buffer;
std::cout << " -BadDerived (freed)\n";
}
};
/* CORRECT: virtual destructor */
class GoodBase {
public:
GoodBase() { std::cout << " +GoodBase\n"; }
virtual ~GoodBase() { std::cout << " -GoodBase\n"; }
};
class GoodDerived : public GoodBase {
int *buffer;
public:
GoodDerived() : buffer(new int[1000]) {
std::cout << " +GoodDerived (4000 bytes)\n";
}
~GoodDerived() override {
delete[] buffer;
std::cout << " -GoodDerived (freed)\n";
}
};
int main() {
std::cout << "non-virtual base:\n";
BadBase *p = new BadDerived();
delete p; /* only ~BadBase runs: LEAK */
std::cout << "\nvirtual base:\n";
GoodBase *q = new GoodDerived();
delete q; /* both destructors run */
std::cout << "\nno pointer involved (both fine):\n";
{ BadDerived direct; }
return 0;
}
Output:
non-virtual base:
+BadBase
+BadDerived (4000 bytes)
-BadBase
<-- ~BadDerived NEVER RAN: 4000 bytes leaked
virtual base:
+GoodBase
+GoodDerived (4000 bytes)
-GoodDerived (freed)
-GoodBase
no pointer involved (both fine):
+BadBase
+BadDerived (4000 bytes)
-BadDerived (freed)
-BadBase
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class Employee {
protected:
std::string name;
double basic;
public:
Employee(std::string n, double b)
: name(std::move(n)), basic(b) {}
/* virtual destructor: this class IS a polymorphic base */
virtual ~Employee() = default;
virtual double salary() const { return basic; }
virtual std::string role() const { return "Employee"; }
void payslip() const {
std::cout << " " << role() << " " << name
<< ": Rs " << salary() << "\n";
}
};
class Manager : public Employee {
double allowance;
public:
Manager(std::string n, double b, double a)
: Employee(std::move(n), b), allowance(a) {}
double salary() const override { return basic + allowance; }
std::string role() const override { return "Manager "; }
};
class Engineer : public Employee {
int overtimeHours;
static constexpr double RATE = 250.0;
public:
Engineer(std::string n, double b, int h)
: Employee(std::move(n), b), overtimeHours(h) {}
double salary() const override {
return basic + overtimeHours * RATE;
}
std::string role() const override { return "Engineer "; }
};
/* MULTILEVEL: a lead engineer is an engineer */
class LeadEngineer : public Engineer {
double leadBonus;
public:
LeadEngineer(std::string n, double b, int h, double bonus)
: Engineer(std::move(n), b, h), leadBonus(bonus) {}
double salary() const override {
return Engineer::salary() + leadBonus; /* reuse */
}
std::string role() const override { return "Lead Eng "; }
};
int main() {
std::vector<std::unique_ptr<Employee>> staff;
staff.push_back(std::make_unique<Employee>("Ram", 40000));
staff.push_back(std::make_unique<Manager>("Sita", 60000, 15000));
staff.push_back(std::make_unique<Engineer>("Hari", 55000, 12));
staff.push_back(std::make_unique<LeadEngineer>(
"Gita", 70000, 8, 20000));
double payroll = 0;
for (const auto &e : staff) { e->payslip(); payroll += e->salary(); }
std::cout << " ---------------------------------\n";
std::cout << " monthly payroll: Rs " << payroll << "\n";
std::cout << " annual payroll : Rs " << payroll * 12 << "\n";
/* unique_ptr deletes through Employee* β safe only
because ~Employee is virtual */
return 0;
}
Output:
Employee Ram: Rs 40000
Manager Sita: Rs 75000
Engineer Hari: Rs 58000
Lead Eng Gita: Rs 92000
---------------------------------
monthly payroll: Rs 265000
annual payroll : Rs 3.18e+06
#include <iostream>
#include <stdexcept>
class Rect {
double w, h;
void validate() const {
if (w <= 0 || h <= 0)
throw std::invalid_argument("non-positive side");
}
public:
Rect(double a, double b) : w(a), h(b) { validate(); }
Rect(double s) : Rect(s, s) { } /* delegates */
Rect() : Rect(1, 1) { } /* delegates */
double area() const { return w * h; }
};
class Base {
public:
Base(int a) { std::cout << " Base(int " << a << ")\n"; }
Base(int a, int b) { std::cout << " Base(" << a << ","
<< b << ")\n"; }
};
class Derived : public Base {
int extra = 99; /* default member init */
public:
using Base::Base; /* inherit both ctors */
int getExtra() const { return extra; }
};
int main() {
Rect a(4, 5), b(3), c;
std::cout << " areas: " << a.area() << " "
<< b.area() << " " << c.area() << "\n";
try { Rect bad(-2); }
catch (const std::invalid_argument &e) {
std::cout << " caught: " << e.what()
<< " (validated once, in one place)\n";
}
Derived d1(7);
Derived d2(3, 4);
std::cout << " inherited ctors work; extra = "
<< d1.getExtra() << "\n";
return 0;
}
Output:
areas: 20 9 1
caught: non-positive side (validated once, in one place)
Base(int 7)
Base(3,4)
inherited ctors work; extra = 99
Note that Rect bad(-2) threw even though the validation lives only in the two-argument constructor β the delegation routes through it. And extra was still initialised to 99 by its default member initialiser, because inherited constructors do not know about the derived class's own members.
operator= are never inherited, and that a base constructor must be called from the initialiser list, not the body.Create a free account to tick topics off, take notes as you read, watch the video lessons and get a day-by-day study plan built around your exam date.
Loadingβ¦