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;}
- [C/C++]继承、多态和虚函数整理
- C++虚函数和多态继承
- C继承和多态
- 继承和虚函数
- [C++]virtual关键字:虚函数,虚继承和虚基类
- 【C++】虚函数和虚继承的内存分布情况
- 【足迹C++primer】52、转换和继承,虚函数
- More Effective C++----(24)理解虚拟函数、多继承、虚继承和RTTI所需的代价
- C++,继承、虚函数解惑!
- C++,继承、虚函数解惑!
- 【继承与多态】C++:继承中的赋值兼容规则,子类的成员函数,虚函数(重写),多态
- 【继承与多态】C++:继承中的赋值兼容规则,子类的成员函数,虚函数(重写),多态
- 【C++】继承和多态之——菱形继承
- C++ 多继承和虚继承的内存布局
- 多态之一(继承和虚函数)
- 【C++】c++单继承、多继承、菱形继承内存布局(虚函数表结构)
- 【C++】c++单继承、多继承、菱形继承内存布局(虚函数表结构)
- 多继承,RTTI和虚函数
- 深入阅读Mina源码(3) —— Mina之IOAdapter(二)
- 进程控制编程
- Android四大组件 BroadCasrReciver
- 如何实现session共享
- bzoj3237[Ahoi2013]连通图 cdq分治+并查集
- C++虚函数和多态继承
- Temporal Activity Detection in Untrimmed Videos with Recurrent Neural Networks
- 补码(为什么按位取反再加一):告诉你一个其实很简单的问题
- Android-Binder系统APP实现
- 关于Prime算法的从入门到升天的讲解(带模板)
- Java的变量和数据类型
- 【bzoj3669】[Noi2014]魔法森林
- sklearn因子分析(python)
- poj3349——Snowflake Snow Snowflakes