[ 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 Account
s.
Example 22.3. src/derivation/assigcopy/bank.h
Example 22.4. src/derivation/assigcopy/account.h
[ . . . . ] class Account { public: Account(unsigned acctNum, double balance, QString owner); virtual ~Account(){ qDebug() << "Closing Acct - sending e-mail " << "to primary acctholder:" << m_Owner; } virtual QString getName() const {return m_Owner;} // other virtual functions private: unsigned m_AcctNum; double m_Balance; QString m_Owner; }; class JointAccount : public Account { public: JointAccount (unsigned acctNum, double balance, QString owner, QString jowner); JointAccount(const Account & acct, QString jowner); ~JointAccount() { qDebug() << "Closing Joint Acct - sending e-mail " << "to joint acctholder:" << m_JointOwner; } QString getName() const { return QString("%1 and %2").arg(Account::getName()) .arg(m_JointOwner); } // other overrides private: QString m_JointOwner; }; [ . . . . ]
Bank
can perform uniform operations on its collected Account
s by calling virtual
methods on each one.
In Example 22.5, delete acct
causes an indirect call to the destructor of Account
and the subsequent release of allocated memory.
Example 22.5. src/derivation/assigcopy/bank.cpp
[ . . . . ] #include <QDebug> #include "bank.h" #include "account.h" Bank::~Bank() { qDeleteAll(m_Accounts); m_Accounts.clear(); } Bank& Bank::operator<< (Account* acct) { m_Accounts << acct; return *this; } QString Bank::getAcctListing() const { QString listing("\n"); foreach(Account* acct, m_Accounts) listing += QString("%1\n").arg(acct->getName()); return listing; }
Without declaring ~Account()
to be virtual
in the base class, you would get an incorrect result from running Example 22.6.[88]
Example 22.6. src/derivation/assigcopy/bank.cpp
[ . . . . ] int main() { QString listing; { Bank bnk; Account* a1 = new Account(1, 423, "Gene Kelly"); JointAccount* a2 = new JointAccount(2, 1541, "Fred Astaire", "Ginger Rodgers"); JointAccount* a3 = new JointAccount(*a1, "Leslie Caron"); bnk << a1; bnk << a2; bnk << a3; JointAccount* a4 = new JointAccount(*a3); bnk << a4; listing = bnk.getAcctListing(); } qDebug() << listing; qDebug() << "Now exit program" ; }
Closing Acct - sending e-mail to primary acctholder:Gene Kelly Closing Acct - sending e-mail to primary acctholder:Fred Astaire Closing Acct - sending e-mail to primary acctholder:Gene Kelly Closing Acct - sending e-mail to primary acctholder:Gene Kelly [ ... ]
By making the destructor virtual
, both types of Account
will get destroyed properly
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 Closing Joint Acct - sending e-mail to joint acctholder:Leslie Caron Closing Acct - sending e-mail to primary acctholder:Gene Kelly Closing Joint Acct - sending e-mail to joint acctholder:Leslie Caron Closing Acct - sending e-mail to primary acctholder:Gene Kelly [ ... ]
Note | |
---|---|
If you declare one or more |
[88] 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.
Generated: 2012-03-02 | © 2012 Alan Ezust and Paul Ezust. |