C++primer第十三章继承类
1.RatedPlayer
main
#include "tabtenn0.hpp"
int main()
{
using std::cout;
using std::endl;
TableTennisPlayer player1("Jay", "Chou", false);
RatedPlayer rplayer1(1140, "Jiang", "Huan", true);
rplayer1.Name();
if(rplayer1.HasTable())
cout << ": has a table.\n";
else
cout << ": hasn't a table.\n";
player1.Name();
if(player1.HasTable())
cout << ": has a table.\n";
else
cout << ": hasn't a table.\n";
cout << "Name: ";
rplayer1.Name();
cout << "; Rating: " << rplayer1.Rating() << endl;
RatedPlayer rplayer2(1212, player1);
cout << "Name: ";
rplayer2.Name();
cout << "; Rating: " << rplayer2.Rating() << endl;
return 0;
}
.hpp
#ifndef tabtenn0_hpp
#define tabtenn0_hpp
#include <stdio.h>
#include <string>
#include <iostream>
using std::string;
using std::cout;
class TableTennisPlayer
{
private:
string firstname;
string lastname;
bool hasTable;
public:
TableTennisPlayer(const string & fn = "none", const string & ln = "none", bool ht = false);
void Name() const {cout << lastname << ", " << firstname;}
bool HasTable() const {return hasTable;}
void ResetTable(bool v) { hasTable = v;}
};
class RatedPlayer : public TableTennisPlayer
{
private:
unsigned int rating;
public:
RatedPlayer(unsigned int r = 0, const string & fn = "none", const string & ln = "none", bool ht = false);
RatedPlayer(unsigned int r, const TableTennisPlayer & tp);
unsigned int Rating(){return rating;}
void ResetRating (unsigned int r) {rating = r;}
};
#endif /* tabtenn0_hpp */
.cpp
#include "tabtenn0.hpp"
TableTennisPlayer::TableTennisPlayer(const string & fn, const string & ln, bool ht)
{
firstname = fn;
lastname = ln;
hasTable = ht;
}
RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht):TableTennisPlayer(fn, ln, ht)
{
rating = r;
}
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp):rating(r),TableTennisPlayer(tp)
{ // 调用基类复制构造函数
}
2.引用与指针在派生类和基类之间的关系
TableTennisPlayer player1("Jay", "Chou", false);
TableTennisPlayer & rt = rplayer1;
TableTennisPlayer *pt = &rplayer1;
rt.Name();
pt->Name();
可以看到 可以用基类的引用或者指针引用或指向派生类对象。反之,则不可以。也就是派生类不能指向基类。
包括函数里面的形参如果是基类引用或者指针,它可以兼容派生类
void Show(const TableTennisPlayer &rt)
{
using std::cout;
cout << "name: ";
rt.Name();
cout<< " table: ";
if(rt.HasTable())
cout << "yes\n";
else
cout << "No\n";
}
......
TableTennisPlayer player1("Jay", "Chou", false);
RatedPlayer rplayer1(1140, "Jiang", "Huan", true);
Show(player1);
Show(rplayer1);
// 指针
void Show(const TableTennisPlayer *rt)
{
using std::cout;
cout << "name: ";
rt->Name();
cout<< " table: ";
if(rt->HasTable())
cout << "yes\n";
else
cout << "No\n";
}
TableTennisPlayer player1("Jay", "Chou", false);
RatedPlayer rplayer1(1140, "Jiang", "Huan", true);
Show(&player1);
Show(&rplayer1);
TableTennisPlayer player2(rplayer1);
TableTennisPlayer player3 = rplayer1;
这也可以,因为TableTennisPlayer默认复制构造函数:TableTennisPlayer(const TableTennisPlayer &);赋值构造函数:TableTennisPlayer & operator=(const TableTennisPlayer &)const;
所以都是基类是可以兼容派生类的,但反之当然不行。
3.Brass和BrassPlus
main.cpp
//#include <iostream>
//#include "brass.hpp"
//int main(int argc, const char * argv[]) {
// using std::cout;
// using std::endl;
//
// Brass Piggy("Porcelot Pigg", 381299, 4000.00);
// BrassPlus Hoggy("Horatio Hogg", 382288, 3000.00);
// Piggy.ViewAcct();
// cout << endl;
// Hoggy.ViewAcct();
// cout << endl;
// cout << "Depositing $1000 into the Hogg Account:\n";
// Hoggy.Deposit(1000.0);
// cout << "New balance: $" << Hoggy.Balance() << endl;
// cout << "Withdrawing $4200 from the Pigg Account:\n";
// Piggy.Withdraw(4200.00);
// cout << "Pigg account balance: $" << Piggy.Balance() << endl;
// cout << "Withdrawing $4200 from the Hogg Account:\n";
// Hoggy.Withdraw(4200.00);
// Hoggy.ViewAcct();
// return 0;
//}
#include <iostream>
#include <string>
#include "brass.hpp"
const int CLIENTS = 4;
int main()
{
using namespace std;
Brass * p_clients[CLIENTS];
string temp;
long tempnum;
double tempbal;
char kind;
for (int i = 0; i < CLIENTS; i++)
{
cout << "Enter client's name: ";
getline(cin, temp);
cout << "Enter client's account number: ";
cin >> tempnum;
cout << "Enter opening balance: $";
cin >> tempbal;
cout << "Enter 1 for Brass Account or 2 for BrassPlus Account: ";
while (cin >> kind && (kind != '1' && kind != '2')) {
// 如果输入正常且kind不等于1和2则重新输入
cout << "Enter either 1 or 2: ";
}
if (kind == '1') {
p_clients[i] = new Brass(temp, tempnum, tempbal);
}
else
{
double tmax, trate;
cout << "Enter the overdraft limit: $";
cin >> tmax;
cout << "Enter the interst rate as a decimal fraction: ";
cin >> trate;
p_clients[i] = new BrassPlus(temp, tempnum, tempnum, tmax, trate);
}
while (cin.get()!='\n')
continue;
cout << endl;
}
for (int i = 0; i < CLIENTS; i++) {
p_clients[i]->ViewAcct();
cout << endl;
}
for (int i = 0; i < CLIENTS; i++) {
delete p_clients[i];
}
cout << "Done.\n";
return 0;
}
.hpp
#ifndef brass_hpp
#define brass_hpp
#include <stdio.h>
#include <string>
using std::string;
class Brass
{
private:
std::string fullName; // 客户姓名
long acctNum; // 账号
double balance; // 当前结余
public:
Brass(const std::string & s = "Nullbody", long an = -1, double bal = 0.0);
void Deposit(double amt); // 存款
virtual void Withdraw(double amt); // 取款
double Balance() const; // 结余
virtual void ViewAcct() const; // 显示用户信息
virtual ~Brass(){}
};
class BrassPlus : public Brass
{
private:
double maxLoan; // 透支上限
double rate; // 透支贷款率
double owesBank; // 当前透支额度
public:
BrassPlus(const std::string & s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.11125);
BrassPlus(const Brass &ba, double ml = 500, double r = 0.11125);
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 /* brass_hpp */
.cpp
#include "brass.hpp"
#include <iostream>
using std::cout;
using std::endl;
using std::string;
// 格式化输出
typedef std::ios_base::fmtflags format;
typedef std::streamsize precis;
format setFormat()
{ // 17章会学习 先熟悉
// set up ###.##
return cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
}
void restore(format f, precis p)
{
cout.setf(f, std::ios_base::floatfield);
cout.precision(p);
}
Brass::Brass(const string & s, long an, double bal)
{
fullName = s;
acctNum = an;
balance = bal;
}
void Brass::Deposit(double amt)
{
// 存款
if(amt < 0)
cout << "Negative deposit not alloweed; "
<< "desposit is cancelled.\n";
else
balance += amt;
}
void Brass::Withdraw(double amt)
{
format initialState = setFormat();
precis prec = cout.precision(2);
if(amt < 0)
cout << "Withdrawal amout must be positive; "
<< "withdrawal canceled.\n";
else if (amt <= balance)
balance -= amt;
else
cout << "Withdrawal amount of $" << amt
<< " exceeds your balance.\n"
<< "Withdrawal canceled.\n";
restore(initialState, prec);
}
double Brass::Balance() const
{
return balance;
}
void Brass::ViewAcct() const
{
format initialState = setFormat();
precis prec = cout.precision(2);
cout << "Client:" << fullName << endl;
cout << "Account Number: " << acctNum << endl;
cout << "Balance: $" << balance << endl;
restore(initialState, prec);
}
BrassPlus::BrassPlus(const std::string & s, long an, double bal, double ml, double r):Brass(s, an, bal)
{
maxLoan = ml;
owesBank = 0.0;
rate = r;
}
BrassPlus::BrassPlus(const Brass &ba, double ml, double r):Brass(ba)
{
maxLoan = ml;
owesBank = 0.0;
rate = r;
}
void BrassPlus::ViewAcct() const
{
format initialState = setFormat();
precis prec = cout.precision(2);
Brass::ViewAcct();
cout << "Maximum loan: $" << maxLoan << endl;
cout << "Owed to bank: $" << owesBank << endl;
cout.precision(3);
cout << "Loan Rate: " << 100*rate << "%\n";
restore(initialState, prec);
}
void BrassPlus::Withdraw(double amt)
{
format initialState = setFormat();
precis prec = cout.precision(2);
double bal = Balance();
if(amt <= bal)
Brass::Withdraw(amt);
else if (amt <= bal + maxLoan - owesBank)
{
double advance = amt - bal;
owesBank +=advance * (1 + rate);
cout << "Bank advance: $" << advance << endl;
cout << "Finance charge: $" << advance * rate << endl;
Deposit(advance);
Brass::Withdraw(amt);
}
else
cout << "Credit limit exceeded.";
restore(initialState, prec);
}
在Brass类中可以看到析构函数也是虚函数,这样就可以使BrassPlus指针,在析构的时候,可以调用基类的析构函数进行析构。
在派生类中调用基类同样的函数时,加上作用域解析符号::
4.静态联编和动态联编
BrassPlus b1;
Brass *bp;
bp = &b1;
bp->ViewAcct();
用上面的例子来说,如果Brass类中没有声明虚函数ViewAcct(),那么在编译阶段其实就能确定, bp->ViewAcct()调用的是Brass::ViewAcct()。这个就是静态联编,
而如果申明了虚函数,那么对象类型为BrassPlus,通常需要在运行阶段才能确定类型,并把bp->ViewAcct()关联成BrassPlus::ViewAcct()。对虚方法就是用的动态联编。
5.虚函数的工作原理
给每个对象添加一个隐藏成员,隐藏成员中保存了一个指向函数地址数组的指针,这个函数就叫虚函数表。
也就是有个表会记录你的虚构函数,比如你Brass类好几个虚函数,他会记录下你虚函数的地址,如果你的BrassPlus有对基类Brass的虚函数有更新,那么地址也会更新,没有更新的虚函数,就保存这原来基类虚函数的地址,当然BrassPlus有新的虚函数,也是会新增到地址上去。
就比如说 第三点中代码中有虚析构函数,BrassPlus类没有写析构函数,那么他会按照基类的析构函数地址去执行。