C++虚函数和多态继承

指针、引用和虚函数

指针和引用是很好理解的,变量的引用相当于给变量取了个别名,在函数调用时,传引用是会将参数列表的值改变的。

#include <iostream>
using namespace std;
int main()
{
     int m=6;
    int &p=m;
    int *q = &m;
    cout<<&p<<endl;
    cout<<q<<endl;
    return 0;
}


最终输出的结果都是0x7ffdd9aff7b4,即为内存中存储整型变量m的地址。对于虚函数,它是实现多态公有继承的关键。什么是多态呢?我们的派生类是可以使用基类的方法的,但是如果只是使用基类的方法,我们是没有对基类方法作任何修改的,但是我们会遇到这种情况,我们希望一个方法在基类和派生类中的行为是不一样的,即方法的行为应取决于调用该对象的方法,这就是多态。实现多态有有种重要机制:(1)在派生类中重新定义基类的方法(2)使用虚方法。对于虚函数的介绍,这篇博客写的不错:虚函数,指针和引用
稍带提到一点,虽然 一个函数定义为虚函数,那么无论它传下多少层,都将保持为虚函数,而不必每次都加关键字virtual。但是在实际的过程中加上virtual关键字是有必要的,这有有利于代码的阅读

多态共有继承的实例

这是一个C++ Primary上简单的例子,需要开发两个类,基类Brass和派生类BrassPlus。在派生类中增加了几个私有数据maxLoan、rate和owesBank。我们不必知道这些变量的含义,我们不关心方法的实现,而是更关心多态性。两个类的定义如下:

#ifndef BRASS_H
#define BRASS_H
#include<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_H

这里面我们可以看到函数ViewAcct() const和Withdraw(double amt)都被定义为虚函数。也就是表明这个函数在基类和派生类中的行为是不一样,它们的实现如下:

#include "brass.h"
#include<iostream>
using namespace std;

typedef ios_base::fmtflags format;
typedef streamsize precis;
format setFormat();
void restore(format f,precis 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 allowed; "<<"deposit is cancelled.\n";
    else
        balance += amt;
}

void Brass::Withdraw(double amt)
{
    format initialState = setFormat();
    precis prec = cout.precision(2);
    if(amt<0)
        cout<<"Withdraw amount must be positive; "<<"withdrawal cancelled.\n";
    else if(amt<=balance)
        balance -= amt;
    else
        cout<<"Withdrawal amount of $"<<amt<<" exceeds your balance.\n"<<"Withdrawal cancelled.\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 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<<"Maximim 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 initalState = 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.0+rate);
        cout<<"Bank advance: $"<<advance<<endl;
        cout<<"Finance charge: $"<<advance*rate<<endl;
        Deposit(advance);
        Brass::Withdraw(amt);
    }
    else
        cout<<"Credit limit exceeded.Transaction cancelled.\n";
    restore(initalState,prec);
}

format setFormat()
{
    return cout.setf(ios_base::fixed,ios_base::floatfield);
}

void restore(format f, precis p)
{
    cout.setf(f,ios_base::floatfield);
    cout.precision(p);
}

这里我们可以看到在定义ViewAcct()中,使用到了作用域解析运算符,假设在BrassPlus中定义ViewAcct()没有用作用域解析运算符会怎么样呢?

void BrassPlus::ViewAcct() const
{
...
  ViewAcct();
...
}

这样会导致一个问题,这里会默认调用的是BrassPlus::ViewAcct(),这样就会变成一个递归调用,是一个不会终止的函数。
在我们的主函数中调用ViewAcct()方法和Withdraw()方法时,则不用显示的调用,编译器会根据你定义的对像确定自己的行为。下面就是一个例子:

#include <iostream>
#include"brass.h"


using namespace std;

int main()
{
    Brass Piggy("Porcelot Pigg",381299,4000.0);
    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.00);
    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;
}

上面是通过对象调用的,而不是通过指针或引用,没有使用虚方法的特性。由于使用的是公有继承模型,因此Brass指针既可以指向Brass对象,也可以指向BrassPlus对象。所以可以使用一个数组来表示多种类型的对象,这就是多态性(隐士的强制转换)。代码如下:

#include<iostream>
#include"brass.h"
#include<string>
using namespace std;

const int CLIENTS = 4;
int main()
{
    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'))
            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 interest rate as a decimal fraction: ";
            cin>>trate;
            p_clients[i] = new BrassPlus(temp,tempnum,tempbal,tmax,trate);
        }
        while(cin.get()!='\n')
            continue;
    }
    cout<<endl;
    for(int i=0;i<CLIENTS;i++)
    {
        delete p_clients[i];
    }
    cout<<"Done.\n";
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小方爱自律

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值