类继承(2)

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:若析构函数不是虚函数,则指针和引用的类型将决定所调用的类函数,当基类指针(引用)指向派生类时,只会调用基类的析构函数,即只释放基类部分的内存而派生类部分的内存将出现泄露问题。若析构函数是虚函数,则指针和引用所指向对象的类型将决定所调用的类函数,因此不会出现上述问题。

参考文章:C/C++基类的析构函数为什么必须定义为虚函数?_一只笨鸟的博客-CSDN博客_析构函数为什么是虚函数https://blog.csdn.net/komtao520/article/details/82424468

  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();
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值