C++虚函数和多态继承

来源:互联网 发布:vc ado access数据库 编辑:程序博客网 时间:2024/05/21 15:05

指针、引用和虚函数

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

#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