避免在继承体系中做向下转型动作(Effectice c++)条款39

此篇博客基于《Effective c++》一书的39条款,采用的例子与书上类似,但是在博客中会有更多我自己粗浅的见解,尤其是在看过设计模式后,对此条款提到的拓展性会有更好的认识。
首先介绍一个转型动作dynamic_cast,在c++中有四种转型动作,本人用过的有static_cast const_cast和上面提到的这个,具体详情在此不作赘述。
在继承体系中,public继承是一种is a 的关系,所以所有父类能够完成的动作,讲道理都是能被子类所替换的,所以在c++中,向上转型是安全的,即子类能够代替父类完成相应的工作。由此也可以推出,向下转型存在着一定的风险,所以这边用到了一个dynamic_cast的转型动作,当你确信该父类指针确实指向子类对象时,可以采用此类型转换,但此篇文章的主题为——避免向下转型动作,下面便介绍两种避免向下转型的方法,首先贴上代码

class Person
{
public:
	Person(string name) :name(name) {}
private:
	string name;
};
class BankAccount
{
public:
	virtual ~BankAccount() {};
	virtual void makeDeposit(int amount) = 0;//存款
	virtual void makeWithdrawal(int amount) = 0;//取款
	virtual int balance() = 0;//余额
private:
};
//带有利息类型的储金账户  
class SavingsAccount :public BankAccount
{
public:
	SavingsAccount(std::shared_ptr ptr) :m_ptr(ptr) {}
	virtual void makeDeposit(int amount)//存款
	{
		cout << "存入" << amount << endl;
	}
	virtual void makeWithdrawal(int amount)//取款
	{
		cout << "取出" << amount << endl;
	}
	virtual int balance()//余额
	{
		return 50;
	}
	int creditInterest()//利息
	{
		interest = 10;
		return interest;
	}
private:
	std::shared_ptr m_ptr;
	int interest;
};
//带利息的支票账户
class CheckingAccount :public BankAccount
{
public:
	CheckingAccount(std::shared_ptr ptr) :m_ptr(ptr) {}
	virtual void makeDeposit(int amount)//存款
	{
		cout << "存入" << amount << endl;
	}
	virtual void makeWithdrawal(int amount)//取款
	{
		cout << "取出" << amount << endl;
	}
	virtual int balance()//余额
	{
		return 50;
	}
	virtual int creditInterest()//利息
	{
		interest = 10;
		return interest;
	}
private:
	std::shared_ptr m_ptr;
	int interest;
};

在此我定义了四个类 其中一个person类用于构造BankAccount的派生类。一个基类加上两个派生类。下面看客户端的调用方法。

void MyFunc1()
{
	std::shared_ptr p_peo = make_shared("whj");
	std::shared_ptr p_bank(new SavingsAccount(p_peo));
	//p_bank->creditInterest();//无法调用,因为creditInterest不是一个虚函数
	SavingsAccount* temp_p = dynamic_cast(p_bank.get());//dynamic_cast安全的向下转型动作,若型别一致,传
																			//回一个该类型,否则传回nulltpr
	cout << temp_p->creditInterest();
}

可以看到,想要调用派生类的creditInterest()必须将基类指针向下转型才可以实现,但是这个向下转型的动作需要客户端手动指定类型。由此我们可以很容易想到,为什么需要有这个转型动作,是由于creditInterest()在基类中美体现,若它是一个纯虚函数,则可以在客户端中通过基类指针调用,这样即解决了向下转型的问题,又可以使得多态得以实现,无需客户端判断具体对象。这就是第一种避免向下转型的方法,但是此方法需要将已经封装好的基类进行修改,违背了开闭原则,而且基类BankAccount的派生类分为有利息的账户和无利息的账户,在基类中声明creditInterest()意味着非利息账户也必须重写此方法(解决办法是在无利息的子类中将其实现为一个空的函数,或者直接在基类中选择将其定义为一个空的函数体而不是纯虚函数),对于有代码洁癖的人来说这实在不是一个好方法。
下面提供第二种避免向下转型的方法,通过一个派生自BankAccount的抽象类InterestAccount ,在它的内部加上所有拥有利息的账户的接口virtual int creditInterest() = 0。如此便可避免强迫在原本的基类中实现本不该有的接口。
代码如下


class InterestAccount :public BankAccount
{
public:
	virtual void makeDeposit(int amount) = 0;//存款
	virtual void makeWithdrawal(int amount) = 0;//取款
	virtual int balance() = 0;//余额
	virtual int creditInterest() = 0;//利息
private:
};
//储金账户,继承自InterestAccount
class SavingsAccount2 :public InterestAccount
{
public:
	SavingsAccount2(std::shared_ptr ptr) :m_ptr(ptr) {}
	virtual void makeDeposit(int amount)//存款
	{
		cout << "存入" << amount << endl;
	}
	virtual void makeWithdrawal(int amount)//取款
	{
		cout << "取出" << amount << endl;
	}
	virtual int balance()//余额
	{
		return 50;
	}
	virtual int creditInterest()//利息
	{
		interest = 10;
		return interest;
	}
private:
	std::shared_ptr m_ptr;
	int interest;
};
//支票账户,继承自InterestAccount
class CheckingAccount2 :public InterestAccount
{
public:
	CheckingAccount2(std::shared_ptr ptr) :m_ptr(ptr) {}
	virtual void makeDeposit(int amount)//存款
	{
		cout << "存入" << amount << endl;
	}
	virtual void makeWithdrawal(int amount)//取款
	{
		cout << "取出" << amount << endl;
	}
	virtual int balance()//余额
	{
		return 50;
	}
	virtual int creditInterest()//利息
	{
		interest = 10;
		return interest;
	}
private:
	std::shared_ptr m_ptr;
	int interest;
};

当然,可能存在某种情况,比如你不能修改BankAcount类,或者你甚至不能继承它(继承一个类需要有它的具体实现细节)此时就只能考虑使用向下转型了,但即便如此,也应该避免使用static_cast,而是使用安全向下转型的dynamic_cast动作。

你可能感兴趣的:(个人学习,C++,继承)