“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201088
Exercise
Write a base class CPlayer andthree classes
CAmazon, CPaladin, and CSorceress
derived from CPlayer to make the left
program show the following result:
I am an amazon!I am a paladin!I am a sorceress!
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201089
Exercise
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201090
Exercise
I am an amazon!I am a paladin!I am a sorceress!
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201091
Exercise
We have 0 amazons, 0 paladins, and 0 sorceress.We have 1 amazons, 1 paladins, and 1 sorceress.We have 2 amazons, 3 paladins, and 1 sorceress.We have 1 amazons, 1 paladins, and 1 sorceress.We have 0 amazons, 1 paladins, and 1 sorceress.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201092
Declare Destructors Virtual inPolymorphic Base Classes.
int main() {CPlayer *p[3] = {new CAmazon,
new CPaladin,new CSorceress};
for (int i=0; i<3; ++i) {p[i]->Display();
}
// ...
for (int i=0; i<3; ++i) {delete p[i];
}}
class CPlayer{public:
CPlayer() { ... }~CPlayer() { ... }virtual void Display() = 0;
// ...};class CAmazon : public CPlayer {public:
virtual void Display() { }// ...};class CPaladin : public CPlayer {public:
virtual void Display() { }// ...};class CSorceress : public CPlayer {
virtual void Display() { }// ...};
It is an undefined behavior to delete aderived class object through a baseclass pointer with non-virtual destructor.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201093
Declare Destructors Virtual inPolymorphic Base Classes.Declare the destructor virtual when your class
contains at least one virtual member function.class CPlayer {public:
CPlayer() { ... }virtual ~CPlayer() { ... }virtual void Display() = 0;
// ...};// ...
int main() {CPlayer *p[3] = {new CAmazon,
new CPaladin,new CSorceress};
// ...for (int i=0; i<3; ++i) {
delete p[i];}
}Now its behavior is correct
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201094
Declare Destructors Virtual inPolymorphic Base Classes. For those classes that are not designed as base classes. Write
the document clearly to tell the clients not to inherit from it.
class SpecialString: public std::string{// ...};int main(){
SpecialString *pss = new SpecialString(“Ah...”);std::string *ps;// ...ps = pss;// ...delete ps;
}
std::string is not designed to be a baseclass. You may use composition rather thaninheritance.
•A simple “derivation prohibition strategy”is to make all constructors private and provide a publicstatic method to instantiate the object (like a factory function).•Another strategy resorts to friend and virtual inheritance.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201098
Declare Destructors Virtual inPolymorphic Base Classes. For those base classes that are not designed for polymorphic
use (they do not have virtual member functions),make their destructors protected to avoid misuse of polymorphism non-virtual to avoid unnecessary increase of object size
int main() {cout << sizeof(CPoint) << ' ' << sizeof(CLargePoint);return 0;
}
class CLargePoint {public:
virtual ~CLargePoint() {}private:
int x, y;};
class CPoint {protected:
~CPoint() {}private:
int x, y;};
The size of CLargePoint objects gets larger since the invocation of virtual member functionsis usually achieved by a virtual table pointer (vptr) to a virtual table of virtual member functions.
8 12
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 201099
Never Call Virtual Functions duringConstruction and Destruction.
class Transaction {public:
Transaction();virtual void logTransaction() const = 0;
// ...};Transaction::Transaction(){
// ...logTransaction();
}
class BuyTransaction: public Transaction {public:
virtual void logTransaction() const;};class SellTransaction: public Transaction {public:
virtual void logTransaction() const;};
What’s the problem here?
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010100
Never Call Virtual Functions duringConstruction and Destruction.Construction of a derived class object
Step1. Calling the constructors of base class(es) followingthe order in which they appear in the class derivation list.
Step2. Calling the constructors for non-static member classobjects following the order in which they are declared.
Step3. Entering the computation phase of the constructor ofthe derived class.
class Derived: public Base1, public Base2 {public:
Derived() {// computation phase
}private:
Component1 c1;Component2 c2;
};
Steps of Construction
Base1()Base2()Component1()Component2()// computation phase
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010101
Never Call Virtual Functions duringConstruction and Destruction. During execution of constructors of the base class, virtual
functions of the derived class will not be invoked. Rationale:
Calling virtual functions of the derived class before the derivedpart is initialized is not reasonable.
Standard:During construction of base part, the type of object is the baseclass rather than the derived class.class Transaction {public:
Transaction();virtual void logTransaction() const = 0;
// ...};Transaction::Transaction() {
// ...logTransaction();
}
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010102
Never Call Virtual Functions duringConstruction and Destruction.Destruction of a derived class object
The execution follows the reverse order of construction.
class Derived: public Base1, public Base2 {public:
Derived() {// computation phase
}private:
Component1 c1;Component2 c2;
};
Steps of Destruction
// computation phase~Component2()~Component1()~Base2()~Base1()
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010103
Never Call Virtual Functions duringConstruction and Destruction. During execution of destructors of the base class, virtual
functions of the derived class will not be invoked. Rationale:
Calling virtual functions of the derived class after the derivedpart is destroyed is not reasonable.
Standard:During destruction of base part, the type of object is the baseclass rather than the derived class.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010104
Never Call Virtual Functions duringConstruction and Destruction.Can the compiler/linker help us?
In some simple cases, they might be able to detect it.
class Transaction {public:
Transaction();virtual void logTransaction()
const = 0;// ...};Transaction::Transaction(){
// ...logTransaction();
}
class BuyTransaction: public Transaction
{public:virtual void logTransaction() const{}
};class SellTransaction
: public Transaction{public:virtual void logTransaction() const{}
};
int main(){
BuyTransaction bTrans;return 0;
} DevC++4980:In constructor `Transaction::Transaction()': abstract virtual `virtual voidTransaction::logTransaction() const' called from constructor
// compilation warning or linkage error!
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010105
Never Call Virtual Functions duringConstruction and Destruction. But in more complex cases, compiler/linker can not
detect the problem.class Transaction {public:
Transaction();virtual void logTransaction()
const = 0;void legal() { trick(); }
private:void trick(){ logTransaction(); }
};Transaction::Transaction() {
trick();}
class BuyTransaction: public Transaction
{public:virtual void logTransaction() const{}
};class SellTransaction
: public Transaction{public:virtual void logTransaction() const{}
};
int main(){
BuyTransaction bTrans;bTrans.legal();return 0;
}
// no warning or error!The program will terminate with the following message:pure virtual method calledThis application has requested the Runtime to terminate it in an unusual way.Please contact the application's support team for more information.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010106
Never Call Virtual Functions duringConstruction and Destruction. In the worst case, the program just goes “smoothly”
with potential errors.class Transaction {public:
Transaction();virtual void logTransaction()const {
// ...}
private:};Transaction::Transaction() {
logTransaction();}
class BuyTransaction: public Transaction
{public:virtual void logTransaction() const{}
};class SellTransaction
: public Transaction{public:virtual void logTransaction() const{}
};
int main(){
BuyTransaction bTrans;return 0;
}
// no warning or error!
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010107
Never Call Virtual Functions duringConstruction and Destruction. Solutions to post-construction
(immediately calling a virtual function after constructing theentire object)
1. Pass the duty: Just document that user code must do it.2. Post-initialize lazily: Do it during the first call of a member
function. A boolean flag in the base class tells whether ornot post-construction has taken place yet.
3. Pass information from derived class to base class.4. Use a factory function: This way, you can easily force a
mandatory invocation of a post-constructor function.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010108
Never Call Virtual Functions duringConstruction and Destruction. Solutions to post-construction: Post-initialize lazily
class Transaction {public:
Transaction():isPostInit_(false) {}virtual void logTransaction() const { ... }
protected:bool isPostInit_;void PostInit() {
if (!isPostInit_) { logTransaction(); isPostInit_ = true; }}
};
class BuyTransaction: public Transaction {public:
virtual void logTransaction() const {}void AnyMemberFunc() {
PostInit();// ...
}// ...};
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010109
Never Call Virtual Functions duringConstruction and Destruction. Solutions to post-construction: Pass informationclass Transaction {public:
explicit Transaction(const std::string & logInfo);void logTransaction(const std::string &logInfo) const; // non-virtual
private:};Transaction::Transaction() {const std::string &logInfo) {
// ...logTransaction(logInfo);
}
class BuyTransaction: public Transaction {public:
BuyTransaction( parameters ): Transaction( createLogString(parameters) ) { ... }
private:static std::string createLogString( parameters );
};
FAQ1: Why explicit?
FAQ2: Why static?
FAQ1: To prohibit implicit conversion from std::string to Transaction.FAQ2: To avoid using uninitialized data members in the BuyTransaction object.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010110
Never Call Virtual Functions duringConstruction and Destruction. Solutions to post-construction: Use a factory function
class Transaction {public:
Transaction() {}virtual void logTransaction()
const {} // still virtual
template<class T>static T * Create() {
T *p = new T;p->logTransaction();return p;
}// ...};
class BuyTransaction: public Transaction
{public:virtual void logTransaction() const{}
};
class SellTransaction: public Transaction
{public:virtual void logTransaction() const{}
};
int main() {BuyTransaction *p = BuyTransaction::Create<BuyTransaction>();return 0;
}
Constraints: (1) The derived class must not expose any constructor.(2) Construction can be achieved only through new operation.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010111
Copy and Destroy Consistently.
What you create, also clean up:If you define any of the copy constructor (CC), copy assignmentoperator (CA), or destructor (D), you might need to define one orboth of the others.
If you write/disable either CC or CA, you probably need to do thesame for the other since they should have similar effects.
If you explicitly write CC and CA (usu. to allocate or duplicate someresource), you need to deallocate it in D.
If you explicitly write D to manually release a resource, youprobably need to do duplication carefully or to disable copying.
Prefer compiler-generated special members; only these can beclassified as “trivial,”and at least one major STL vendor heavilyoptimizes for classes having trivial special members.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010112
Avoid Slicing.
Is there something wrong?
class Base { ... };class Derived : public Base { ... };
void StrangeFunc(Base obj) { ... }
void PolyFunc(Base &obj) {StrangeFunc(obj);
// ...}int main(){
Derived d;PolyFunc(d);
}
The programmer intends to manipulate Base and Base-derived objects polymorphically.However, when StrangeFunc() is invoked, the object passed in is only the sliced Basepart of the Derived object.
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010113
Avoid Slicing.
In base classes, consider disabling the copyingfunctions.
class Base {// ...protected:
Base(const Base&) { ... }};class Derived : public Base {// ...protected:
Derived(const Derived &rhs):Base(rhs) { ... }};
void StrangeFunc(Base obj) { ... }
int main() {Derived d;StrangeFunc(d);
}// Compilation error
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010114
Avoid Slicing.
Instead, provide a virtual clone function forpolymorphic copying.
class Base {public:
virtual Base* Clone() const = 0;// ...protected:
Base(const Base&) { ... }};class Derived : public Base {public:
virtual Derived* Clone() const { return new Derived(*this); }// ...protected:
Derived(const Derived &rhs):Base(rhs) { ... }};
There is still one problem, a further-derived class in the hierarchy can still forget toimplement Clone().
“Ctor, Dtor, and Assignment,”The Practice of Programming, CSIE@NTNU, 2010115
Avoid Slicing.
We can apply the NonVirtual Interface (NVI) patternand put a valuable assertion.
class Base {public:Base* Clone() const {
Base* p = DoClone();assert( typeid(*p) == typeid(*this)
&& “DoClone incorrect!” );return p;
}// ...protected:
Base(const Base&) { ... }private:
virtual Base* DoClone() const = 0;};
class Derived : public Base{private:
virtual Derived* DoClone() const{
return new Derived(*this);}
};
class DerDer : public Derived{
// Forget to provide DoClone()};
int main() {DerDer dd;Base *p1 = &dd, *p2;p2 = p1->Clone();
} // Causes run-time due to violation of assertion.