virtual与指针和引用的联系,静态联编和动态联编,虚函数使用的注意事项,基类析构函数应当设置为virtual虚函数
//class.h
#pragma once
#ifndef _CLASS_H_
#define _ClASS_H_
#include<string>
using namespace std;
class Brass
{
private:
string fullName;
long acctNum;
double balance;
public:
Brass(const 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 string& s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.11125);
BrassPlus(const Brass& ba, double m1 = 500, double r = 0.11125);
virtual void Withdraw(double amt);
virtual void ViewAcct()const;
void ResetMax(double m)
{
maxLoan = m;
}
void ResetRate(double r)
{
rate = r;
}
void ResetOwes()
{
owesBank = 0;
}
};
#endif
//fuc.cpp
#include"class.h"
#include<iostream>
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" << endl;
}
else
{
balance += amt;
}
}
void Brass::Withdraw(double amt)
{
format initialState = setFormat();
precis prec = cout.precision(2);
if (amt < 0)
{
cout << "Withdrawal amount must be positive;"
<< "Withdrawal cancelled" << endl;
}
else if(amt<=balance)
{
balance -= amt;
}
else
{
cout << "Withdrawal amount of &" << amt
<< "exceeds your balance." << endl;
cout << "Withdrawal cancelled" << endl;
}
restore(initialState, prec);
}
double Brass::Balance()const
{
return balance;
}
void Brass::ViewAcct()const
{
format initialState = setFormat();
precis prec = cout.precision(2);
cout << "Clinet: " << fullName << endl;
cout << "Acount 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;
}
void BrassPlus::ViewAcct()const
{
format initialState = setFormat();
precis prec = cout.precision(2);
Brass::ViewAcct();
cout << "Maximun 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(); //balance是基类中的数据成员,继承类要想访问基类的数据成员必须通过基类的成员函数
//若存款大于想要取款值
if (amt <= bal)
{
Brass::Withdraw(amt);
}
//若取款值大于存款+目前可以借款最大值(maxLoan - owesBank 即理论借款最大值减去目前欠银行款)
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存入预支款后再使用Brass中的Withdraw取款
Deposit(advance);
Brass::Withdraw(amt);
}
restore(initialState, 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);
}
//main.cpp
//在派生类方法中,标准技术是使用作用域解析运算符('::')来调用基类方法
#include<iostream>
#include"class.h"
const int CLIENTS = 4;
int main()
{
Brass* p_clients[CLIENTS]; //指向Brass的指针数组,但可以指向Brass对象以及其继承类BrassPlus的对象
string temp;
long tempnum;
double tempbal;
char kind;
for (int i = 0; i < CLIENTS; i++)
{
cout << "Enter clients name: ";
getline(cin, temp); //为string元素输入 getline(cin,stringname)
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;
// Brass * p_clients = new BrassPlus()
p_clients[i] = new BrassPlus(temp, tempnum, tempbal, tmax, trate);
}
while (cin.get() != '\n')
{
continue;
}
}
cout << endl;
for (int i = 0; i < CLIENTS; i++)
{
p_clients[i]->ViewAcct(); //程序将根据指针所指向的对象来判断使用哪个类的ViewAcct()函数 -- 动态联编
cout << endl;
}
for (int i = 0; i < CLIENTS; i++)
{
delete p_clients[i];
}
cout << "Done" << endl;
system("pause");
return 0;
}
知识点:
1.virtual与指针和引用的联系:
1):如果没有使用关键词virtual,程序将根据引用类型或指针类型选择方法。
2):如果使用了关键词virtual,程序将根据引用或指针指向的对象的类型来选择方法
eg:
//Brass为基类 BrassPlus为继承类
Brass dom;
BrassPlus dot;
Brass & b1 = dom;
Brass & b2 = dot
//1.若类成员函数ViewAccount()不是虚函数
//因为b1,b2的引用类型均为Brass,所以访问的成员函数均为b1::ViewAccount()
b1.ViewAccount();
b2.ViewAccount();
//2.若类成员函数ViewAccout()是虚函数
//因为b1引用的对象是Brass,b2引用的对象是BrassPlus
b1.ViewAccout(); //访问的成员函数为Brass:ViewAccount()
b2.ViewAccout(); //访问的成员函数为BrassPlus:ViewAccout()
2.联编:将源代码中的函数调用解释为执行特定的函数代码块
1):静态联编:在编译过程中完成的联编
2):动态联编:在程序运行时完成的联编(eg:虚函数--程序运行时才可知指针和引用指向对象的类型)
3.虚函数使用的注意事项:
1):在基类中需要声明为虚函数的函数声明前加上virtual,派生类中的virtual可加可不加。
2):构造函数不能是虚函数
3):基类的析构函数应当是虚函数:确保基类对象和派生类对象可以正确的调用析构函数(在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的情况发生)
eg:若析构函数不是虚函数,则指针和引用的类型将决定所调用的类函数,当基类指针(引用)指向派生类时,只会调用基类的析构函数,即只释放基类部分的内存而派生类部分的内存将出现泄露问题。若析构函数是虚函数,则指针和引用所指向对象的类型将决定所调用的类函数,因此不会出现上述问题。
4):友元不能是虚函数,因为友元不是类成员,而只有类成员才能使虚函数
5):基类的虚函数在派生类中重新定义将会隐藏方法,因此若要重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针。同时若基类声明被重载了,则应在派生类中重新定义所有的基类版本。
eg:
class bas
{
private:
public:
//返回值为基类的引用或者指针
virtual bas& test1();
//基类声明重载
virtual void test2(int a);
virtual void test2(double a);
virtual void test2();
};
class der :public bas
{
private:
public:
//返回值可以改为继承类的引用或者指针
virtual der& test1();
//重载部分完全复制
virtual void test2(int a);
virtual void test2(double a);
virtual void test2();
};