欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++primer第十三章继承类

程序员文章站 2023-12-24 16:25:15
...

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类没有写析构函数,那么他会按照基类的析构函数地址去执行。

上一篇:

下一篇: