要使用虚拟析构函数:
https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/1.jpg
不适当的代码将组织动态联编:
5.虚函数的工作原理
https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/2.jpg
https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/3.jpg
https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/4.jpg
6.重新定义隐藏方法(虚方法参数列表不匹配时情况)
如果派生类没有重新定义函数,将使用该函数的基类版本。如果派生类位于派生链中,则将使用最新的虚函数版本,例外的情况是基类版本是隐藏的。
https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/5.jpg
https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/6.jpg
https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/7.jpg
7.protected成员与单设计模式
protected与private相似,在类外只能用公有类成员来访问protected部分中的类成员。他们之间的区别只有在基类派生的类中才会表现出来。派生类的成员可以直接访问基类的保护成员,但不能直接访问基类的私有成员。因此,对于外部世界来说,保护成员的行为与私有成员相似;但对于派生类来说,保护成员的行为与公有成员相似。
关于单设计模式:
https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/8.jpg
https://p-blog.csdn.net/images/p_blog_csdn_net/xuanya0214/EntryImages/20091015/9.jpg
8.抽象基类
abstract base class,ABC。当类声明中包含纯虚函数时, 则不能创建该类的对象。纯虚函数声明的结尾处为=0。包含纯虚函数的类只用作基类,要成为真正的ABC,必须至少包含一个纯虚函数。
view plaincopy to clipboardprint? // acctabc.h -- bank account classes #ifndef ACCTABC_H_ #define ACCTABC_H_ // Abstract Base Class class AcctABC { private: enum {MAX = 35}; char fullName[MAX]; long acctNum; double balance; protected: const char * FullName() const {return fullName;} long AcctNum() const {return acctNum;} std::ios_base::fmtflags SetFormat() const; public: AcctABC(const char *s = "Nullbody", long an = -1, double bal = 0.0); void Deposit(double amt) ; virtual void Withdraw(double amt) = 0; // pure virtual function double Balance() const {return balance;}; virtual void ViewAcct() const = 0; // pure virtual function virtual ~AcctABC() {} }; // Brass Account Class class Brass :public AcctABC { public: Brass(const char *s = "Nullbody", long an = -1, double bal = 0.0) : AcctABC(s, an, bal) { } virtual void Withdraw(double amt); virtual void ViewAcct() const; virtual ~Brass() {} }; //Brass Plus Account Class class BrassPlus : public AcctABC { private: double maxLoan; double rate; double owesBank; public: BrassPlus(const char *s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.10); BrassPlus(const Brass & ba, double ml = 500, double r = 0.1); virtual void ViewAcct()const; virtual void Withdraw(double amt); void ResetMax(double m) { maxLoan = m; } void ResetRate(double r) { rate = r; }; void ResetOwes() { owesBank = 0; } }; #endif // acctabc.h -- bank account classes #ifndef ACCTABC_H_ #define ACCTABC_H_ // Abstract Base Class class AcctABC { private: enum {MAX = 35}; char fullName[MAX]; long acctNum; double balance; protected: const char * FullName() const {return fullName;} long AcctNum() const {return acctNum;} std::ios_base::fmtflags SetFormat() const; public: AcctABC(const char *s = "Nullbody", long an = -1, double bal = 0.0); void Deposit(double amt) ; virtual void Withdraw(double amt) = 0; // pure virtual function double Balance() const {return balance;}; virtual void ViewAcct() const = 0; // pure virtual function virtual ~AcctABC() {} }; // Brass Account Class class Brass :public AcctABC { public: Brass(const char *s = "Nullbody", long an = -1, double bal = 0.0) : AcctABC(s, an, bal) { } virtual void Withdraw(double amt); virtual void ViewAcct() const; virtual ~Brass() {} }; //Brass Plus Account Class class BrassPlus : public AcctABC { private: double maxLoan; double rate; double owesBank; public: BrassPlus(const char *s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.10); BrassPlus(const Brass & ba, double ml = 500, double r = 0.1); virtual void ViewAcct()const; virtual void Withdraw(double amt); void ResetMax(double m) { maxLoan = m; } void ResetRate(double r) { rate = r; }; void ResetOwes() { owesBank = 0; } }; #endif view plaincopy to clipboardprint? // acctabc.cpp -- bank account class methods #include <iostream> #include <cstring> using std::cout; using std::ios_base; using std::endl; #include "acctabc.h" // Abstract Base Class AcctABC::AcctABC(const char *s, long an, double bal) { std::strncpy(fullName, s, MAX - 1); fullName[MAX - 1] = '\0'; acctNum = an; balance = bal; } void AcctABC::Deposit(double amt) { if (amt < 0) cout << "Negative deposit not allowed; " << "deposit is cancelled.\n"; else balance += amt; } void AcctABC::Withdraw(double amt) { balance -= amt; } // protected method ios_base::fmtflags AcctABC::SetFormat() const { // set up ###.## format ios_base::fmtflags initialState = cout.setf(ios_base::fixed, ios_base::floatfield); cout.setf(ios_base::showpoint); cout.precision(2); return initialState; } // Brass methods void Brass::Withdraw(double amt) { if (amt < 0) cout << "Withdrawal amount must be positive; " << "withdrawal canceled.\n"; else if (amt <= Balance()) AcctABC::Withdraw(amt); else cout << "Withdrawal amount of $" << amt << " exceeds your balance.\n" << "Withdrawal canceled.\n"; } void Brass::ViewAcct() const { ios_base::fmtflags initialState = SetFormat(); cout << "Brass Client: " << FullName() << endl; cout << "Account Number: " << AcctNum() << endl; cout << "Balance: $" << Balance() << endl; cout.setf(initialState); } // BrassPlus Methods BrassPlus::BrassPlus(const char *s, long an, double bal, double ml, double r) : AcctABC(s, an, bal) { maxLoan = ml; owesBank = 0.0; rate = r; } BrassPlus::BrassPlus(const Brass & ba, double ml, double r) : AcctABC(ba) // uses implicit copy constructor { maxLoan = ml; owesBank = 0.0; rate = r; } void BrassPlus::ViewAcct() const { ios_base::fmtflags initialState = SetFormat(); cout << "BrassPlus Client: " << FullName() << endl; cout << "Account Number: " << AcctNum() << endl; cout << "Balance: $" << Balance() << endl; cout << "Maximum loan: $" << maxLoan << endl; cout << "Owed to bank: $" << owesBank << endl; cout << "Loan Rate: " << 100 * rate << "%\n"; cout.setf(initialState); } void BrassPlus::Withdraw(double amt) { ios_base::fmtflags initialState = SetFormat(); double bal = Balance(); if (amt <= bal) AcctABC::Withdraw(amt); else if ( amt <= bal + maxLoan - owesBank) { double advance = amt - bal; owesBank += advance * (1.0 + rate); cout << "Bank advance: $" << advance << endl; cout << "Finance charge: $" << advance * rate << endl; Deposit(advance); AcctABC::Withdraw(amt); } else cout << "Credit limit exceeded. Transaction cancelled.\n"; cout.setf(initialState); } // acctabc.cpp -- bank account class methods #include <iostream> #include <cstring> using std::cout; using std::ios_base; using std::endl; #include "acctabc.h" // Abstract Base Class AcctABC::AcctABC(const char *s, long an, double bal) { std::strncpy(fullName, s, MAX - 1); fullName[MAX - 1] = '\0'; acctNum = an; balance = bal; } void AcctABC::Deposit(double amt) { if (amt < 0) cout << "Negative deposit not allowed; " << "deposit is cancelled.\n"; else balance += amt; } void AcctABC::Withdraw(double amt) { balance -= amt; } // protected method ios_base::fmtflags AcctABC::SetFormat() const { // set up ###.## format ios_base::fmtflags initialState = cout.setf(ios_base::fixed, ios_base::floatfield); cout.setf(ios_base::showpoint); cout.precision(2); return initialState; } // Brass methods void Brass::Withdraw(double amt) { if (amt < 0) cout << "Withdrawal amount must be positive; " << "withdrawal canceled.\n"; else if (amt <= Balance()) AcctABC::Withdraw(amt); else cout << "Withdrawal amount of $" << amt << " exceeds your balance.\n" << "Withdrawal canceled.\n"; } void Brass::ViewAcct() const { ios_base::fmtflags initialState = SetFormat(); cout << "Brass Client: " << FullName() << endl; cout << "Account Number: " << AcctNum() << endl; cout << "Balance: $" << Balance() << endl; cout.setf(initialState); } // BrassPlus Methods BrassPlus::BrassPlus(const char *s, long an, double bal, double ml, double r) : AcctABC(s, an, bal) { maxLoan = ml; owesBank = 0.0; rate = r; } BrassPlus::BrassPlus(const Brass & ba, double ml, double r) : AcctABC(ba) // uses implicit copy constructor { maxLoan = ml; owesBank = 0.0; rate = r; } void BrassPlus::ViewAcct() const { ios_base::fmtflags initialState = SetFormat(); cout << "BrassPlus Client: " << FullName() << endl; cout << "Account Number: " << AcctNum() << endl; cout << "Balance: $" << Balance() << endl; cout << "Maximum loan: $" << maxLoan << endl; cout << "Owed to bank: $" << owesBank << endl; cout << "Loan Rate: " << 100 * rate << "%\n"; cout.setf(initialState); } void BrassPlus::Withdraw(double amt) { ios_base::fmtflags initialState = SetFormat(); double bal = Balance(); if (amt <= bal) AcctABC::Withdraw(amt); else if ( amt <= bal + maxLoan - owesBank) { double advance = amt - bal; owesBank += advance * (1.0 + rate); cout << "Bank advance: $" << advance << endl; cout << "Finance charge: $" << advance * rate << endl; Deposit(advance); AcctABC::Withdraw(amt); } else cout << "Credit limit exceeded. Transaction cancelled.\n"; cout.setf(initialState); } 9.继承和动态内存分配 view plaincopy to clipboardprint? //基类 class baseDMA { private: char * label; int rating; public: baseDMA(const char * l = "null", int r = 0); baseDMA(const baseDMA & rs); virtual ~baseDMA(); baseDMA & operator=(const baseDMA & rs); }; //基类 class baseDMA { private: char * label; int rating; public: baseDMA(const char * l = "null", int r = 0); baseDMA(const baseDMA & rs); virtual ~baseDMA(); baseDMA & operator=(const baseDMA & rs); };
如果基类使用动态内存分配,那么这将怎样影响派生类的实现呢。当派生类不使用new时,不需要为派生类定义显式析构函数,复制构造函数和赋值操作符。但是,当基类和派生类都采用动态内存分配时,派生类的析构函数,复制构造函数和赋值操作符都必须使用相应的基类方法来处理基类元素,这种要求是通过3种不同的方式来满足的。
view plaincopy to clipboardprint? //派生类 class hasDMA :public baseDMA { private: char * style; public: ... }; //派生类 class hasDMA :public baseDMA { private: char * style; public: ... };
1)对于析构函数,这是自动完成的。派生类析构函数自动调用基类的析构函数,故其自身的职责是对派生类构造函数执行工作的进行清理。
view plaincopy to clipboardprint? baseDMA::~baseDMA() { delete [] label; } hasDMA::~hasDMA() { delete [] style; } baseDMA::~baseDMA() { delete [] label; } hasDMA::~hasDMA() { delete [] style; }
2)对于构造函数,这是通过在初始化成员列表中调用基类的复制构造函数来完成的,如果不这样做,将自动调用基类的默认构造函数。
view plaincopy to clipboardprint? hasDMA::hasDMA(const hasDMA & hs) : baseDMA(hs) // invoke base class copy constructor { style = new char[std::strlen(hs.style) + 1]; std::strcpy(style, hs.style); } hasDMA::hasDMA(const hasDMA & hs) : baseDMA(hs) // invoke base class copy constructor { style = new char[std::strlen(hs.style) + 1]; std::strcpy(style, hs.style); }
hasDMA复制构造函数只能访问hasDMA的数据,因此它必须调用baseDMA复制构造函数来处理共享的baseDMA数据。没有参数类型为hasDMA引用的baseDMA构造函数,也不需要这样的构造函数。因为基类引用可以指向派生类型。
3)对于赋值操作符,这是通过使用作用域解析操作符显式地调用基类的赋值操作符来完成的。
view plaincopy to clipboardprint? baseDMA & baseDMA::operator=(const baseDMA & rs) { if (this == &rs) return *this; delete [] label; label = new char[std::strlen(rs.label) + 1]; std::strcpy(label, rs.label); rating = rs.rating; return *this; } hasDMA & hasDMA::operator=(const hasDMA & hs) { if (this == &hs) return *this; baseDMA::operator=(hs); // copy base portion style = new char[std::strlen(hs.style) + 1]; std::strcpy(style, hs.style); return *this; } baseDMA & baseDMA::operator=(const baseDMA & rs) { if (this == &rs) return *this; delete [] label; label = new char[std::strlen(rs.label) + 1]; std::strcpy(label, rs.label); rating = rs.rating; return *this; } hasDMA & hasDMA::operator=(const hasDMA & hs) { if (this == &hs) return *this; baseDMA::operator=(hs); // copy base portion style = new char[std::strlen(hs.style) + 1]; std::strcpy(style, hs.style); return *this; } baseDMA::operator=(hs); 是函数表示法,相当于: *this=hs; 10.派生类友元如何访问基类的友元 view plaincopy to clipboardprint? class baseDMA { private: ... public: ... friend std::ostream & operator<<(std::ostream & os, const baseDMA & rs); }; class hasDMA :public baseDMA { private: ... public: ... friend std::ostream & operator<<(std::ostream & os,const hasDMA & rs); }; class baseDMA { private: ... public: ... friend std::ostream & operator<<(std::ostream & os, const baseDMA & rs); }; class hasDMA :public baseDMA { private: ... public: ... friend std::ostream & operator<<(std::ostream & os,const hasDMA & rs); }; view plaincopy to clipboardprint? std::ostream & operator<<(std::ostream & os,const baseDMA & rs) { os << "Label: " << rs.label << std::endl; os << "Rating: " << rs.rating << std::endl; return os; } std::ostream & operator<<(std::ostream & os,const hasDMA & hs) { os << (const baseDMA &) hs; os << "Style: " << hs.style << std::endl; return os; } std::ostream & operator<<(std::ostream & os,const baseDMA & rs) { os << "Label: " << rs.label << std::endl; os << "Rating: " << rs.rating << std::endl; return os; } std::ostream & operator<<(std::ostream & os,const hasDMA & hs) { os << (const baseDMA &) hs; os << "Style: " << hs.style << std::endl; return os; }
因为友元不是成员函数,所以不能使用作用域解析操作符来指出要使用哪个函数。解决方法是使用强制类型转换,以便匹配原型时能够选择正确的函数。
11. 类方法返回对象还是返回引用
在编码方面,直接返回对象与返回引用之间唯一的区别在于函数原型和函数头:
view plaincopy to clipboardprint? Star nova1(const Star &); //returns a Star object Star & nova2(const Star &); //returns a reference to a Star Star nova1(const Star &); //returns a Star object
直接返回对象与按值传递对象相似:它们都生成临时拷贝。同样,返回引用与按引用传递对象相似:调用和被调用的函数对同一个对象进行操作。大部分情况下,返回引用可节省时间和内存,不过并不可以总是返回引用,函数不能返回在函数中创建的临时对象的引用。通用的规则是:如果函数返回在函数中创建的临时对象,则不要使用引用。如果函数返回的是通过引用或指针传递给它的对象,则应按引用返回对象。