[ fromfile: derivedcdas.xml id: derivedcdas ]
Three special kinds of member functions are never inherited:
Copy constructors
Copy assignment operators
Destructors
These three functions are generated automatically by the compiler for classes that do not specify them.
Why Are These Functions Special? | |
---|---|
The base class functions are not sufficient to initialize, copy, or destroy a derived instance. |
For a class that inherits from another, the base class constructor must be called as part of its initialization process. The derived constructor may specify which base class constructor is called in its initialization list.
A class with no constructors is automatically given a compiler-generated, public
, default constructor that calls the default constructor for each of its base classes.
If a class has some constructors but no default constructor, then it has no default initialization.
In this case, any derived class constructor must make an explicit base class constructor call in its initialization list.
Initialization proceeds in the following order:
Base classes first, in the order in which they are listed in the classHead of the derived class
Data members, in declaration order
A public
copy assignment operator is automatically generated by the compiler for each class that does not have one explicitly defined for it.
Because base class data members are generally private, the derived class copy assignment operator must call the base class assignment operator (for each base class) for memberwise copying of those data members to happen. After that, it can perform memberwise assignments of derived class data members.
Other member function operators are inherited the same way as normal member functions.
Like the copy assignment operator,
a public
copy constructor is automatically generated for classes that do not have one defined.
The compiler-generated copy constructor carries out member-by-member initialization by copying the data members of its argument object.
Example 6.20 defined a class with a single constructor that requires three arguments, so Account
has no default constructor
(i.e., the compiler will not generate one).
We declare the base class destructor virtual
to ensure that the appropriate derived class destructor gets called when it is time to destroy a derived object accessed through a base class pointer.
Example 6.20. 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; };
<include src="src/derivation/assigcopy/account.h" mode="cpp" href="src/derivation/assigcopy/account.h" id="assigcopyaccountbase" segid="account"/>
We did not define a copy constructor, which means the compiler will generate one for us.
Therefore, this class can be instantiated in exactly two ways: (1) by calling the three-parameter constructor or (2) by invoking the compiler generated copy constructor and supplying an Account
object argument.
Example 6.21 defines a derived class with two constructors. Both of them require base class initialization.
Example 6.21. src/derivation/assigcopy/account.h
[ . . . . ] 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; };
<include src="src/derivation/assigcopy/account.h" href="src/derivation/assigcopy/account.h" mode="cpp" segid="jointaccount" id="jointaccounth"/>
In Example 6.22, the compiler enables JointAccount::JointAccount
to use Account(const Account&)
for initialization, even though we have not defined it.
The compiler-generated copy constructor does memberwise copy/initialization in the order that the data members are listed in the class definition.
Example 6.22. src/derivation/assigcopy/account.cpp
[ . . . . ] Account::Account(unsigned acctNum, double balance, QString owner) : m_AcctNum(acctNum), m_Balance(balance), m_Owner(owner) { } JointAccount::JointAccount (unsigned acctNum, double balance, QString owner, QString jowner) :Account(acctNum, balance, owner), m_JointOwner(jowner) { } JointAccount::JointAccount (const Account& acc, QString jowner) :Account(acc), m_JointOwner(jowner) { }
<include src="src/derivation/assigcopy/account.cpp" mode="cpp" href="src/derivation/assigcopy/account.cpp" id="accountctors" segid="ctor"/>
Example 6.23 defines a little Bank
class that maintains a list of Account
pointers.
Example 6.23. src/derivation/assigcopy/bank.h
in Example 6.24 the construction of the object a4
makes use of the JointAccount
compiler-supplied copy constructor, which calls the Account
compiler-supplied copy constructor.
Example 6.24. 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; } 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" ; } [ . . . . ]
<include src="src/derivation/assigcopy/bank.cpp" href="src/derivation/assigcopy/bank.cpp" id="bankcdascpp" mode="cpp"/>
Destructors are not inherited. Just as with the copy constructor and copy assignment operator, the compiler generates a destructor if you do not define one explicitly. Base class destructors are automatically called when a derived object is destroyed. Destruction of data members and base class parts occurs in precisely the reverse order of initialization.
Generated: 2012-03-02 | © 2012 Alan Ezust and Paul Ezust. |