22.2.  Polymorphism and virtual destructors

[ fromfile: inheritance-detail.xml id: virtualdestructors ]

When operating on classes in inheritance hierarchies, we often maintain containers of base class pointers that hold addresses of derived objects. Example 22.3 defines a Bank class that has a container of various kinds of Accounts.

Example 22.3. src/derivation/assigcopy/bank.h

#ifndef BANK_H
#define BANK_H
#include <QList>
class Account;

class Bank {
 public:
    Bank& operator<< (Account* acct); 1
    ~Bank();
 private:
    QList<Account*> m_Accounts;
};
#endif

1

This is how we add object ptrs into m_Accounts.

Bank is able to perform uniform operations on its collected Accounts by calling virtual methods on each one.

Example 22.4. src/derivation/assigcopy/bank.cpp

[ . . . . ]

#include "bank.h"
#include "account.h"

Bank::~Bank() {
    foreach (Account* acct, m_Accounts) {
        delete acct;
    }
    m_Accounts.clear();
}

In Example 22.4, delete acct causes an indirect call to the destructor of Account, as well as the subsequent release of allocated memory. However, while every address in the list is an Account, some (perhaps all) might point to derived-class objects and therefore require derived-class destructor calls.

If the destructor is virtual, the compiler allows runtime binding on the destructor call, instead of simply calling Account::~Account() on each one.

Example 22.5. src/derivation/assigcopy/bank.cpp

[ . . . . ]

Bank& Bank::operator<< (Account* acct) {
   m_Accounts << acct;
   return *this;   
}

int main(int argc, char* argv[]) { 
    Bank b;
    Account* a1 = new Account(1, 423, "Gene Kelly");
    JointAccount* a2 = new JointAccount(2, 1541, "Fred Astaire",
    "Ginger Rodgers");
    b << a1;
    b << a2;
} 1

1

At this point, the Bank and both Accounts are destroyed.

Without declaring ~Account() to be virtual in the base class, we would get an incorrect result from running Example 22.5 [64]

Closing Acct - sending e-mail to primary acctholder:Gene Kelly
Closing Acct - sending e-mail to primary acctholder:Fred Astaire

By making the destructor virtual, both types of Account will get destroyed properly and, in this example, both account holders of a joint account will get proper e-mail notifications when the Bank is destroyed.

Closing Acct - sending e-mail to primary acctholder:Gene Kelly
Closing Joint Acct - sending e-mail to joint acctholder:Ginger Rodgers
Closing Acct - sending e-mail to primary acctholder:Fred Astaire
[Note]Note

If you declare one or more virtual methods in a class, you should define a virtual destructor for that class, even if it has an empty body.



[64] Compilers report a missing virtual in the destructor as a warning, and the behavior is undefined, so you may not see the same thing on your system.