条款27:尽量少做转型操作

本文详细解释了C++中类型转化的两大类:旧风与新风,并对比了C++提供的四种新类型转化方式:const_cast、dynamic_cast、reinterpret_cast和static_cast的用途及区别。通过实例演示了在派生类中调用基类成员的正确方法,以及使用dynamic_cast的注意事项。同时,文章强调了避免过度使用类型转化并提出了提高代码质量的建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类型转化分为两大类:旧风格与新风格

旧风格是从C语言继承过来的,分为C-style和function-style。举个例子:

	double d = 1.2;
	int i = (int) d;//C-style
	int j = int(d);//函数风格

C++提供了4种新的类型转化:
const_cast<T> (表达式)
dynamic_cast<T> (表达式)
reinterpret_cast<T> (表达式)
static_cast<T> (表达式)
它们的用处分别如下:
const_cast<T> 用来去除指向某个对象的指针或者引用的const属性。只有它有这个能力。
dynamic_cast<T> 主要用来安全向下转型。这个转型可能耗费很大的成本。
reinterpret_cast<T> 意图执行低级转型,结果取决于编译器,故不可移植。
static_cast用来完成隐式类型转化。


首先,我们倾向于使用新的转型方式。因为1.他很容易被人发现。2.每种方式都有特定的使用范围,这使得编译器很容易判断这种转型是否错误。

然后看一个让人无比纠结的例子:

class Base
{
public:
	Base(int i = 0):bVal(i){cout<<"基类构造函数"<<endl;}
	virtual void say()
	{
		
		cout<<++bVal<<endl;
	}

	int bVal;
};



class Drived:public Base
{
public:
	Drived(int i = 10, int j = 5):Base(i),dVal(j){cout<<"派生类构造函数"<<endl;}
	void say()
	{
		cout<<"基类值为:"<<endl;
		static_cast<Base>(*this).say();		//错误做法
	//	Base::say();						//正确做法	      
		cout<<"派生类值为:"<<endl;
		cout<<dVal<<endl;
	}
private:
	int dVal;
};

其中派生类中同过两种方式来调用基类中的say函数:一种是通过类型转化,一种是直接声明作用域。我们可以用下面的程序检测结果:

int main()
{
	
	Drived d;
	d.say();
	cout<<d.bVal<<endl;
	return 0;
}

虽然static_cast<Base>(*this).say();的打印结果都是11,但是使用cout<<d.bVal<<endl;时,d.bVal的值却不一样了:使用强制类型转化后,d.bVal的还是10;而调用基类函数时,d.bVal的值就变成了11。这中间的问题出在哪里了呢?就是static_cast<Base>(*this).say();是在当前对象基类成分的副本上调用say函数,而不是当前对象本身。所以cout<<++bVal<<endl;修改的是副本的bVal值,而不是d中的。总而言之一句话:当在派生类中想调用基类的某些成分时,直接通过作用域操作符告诉使用的是基类的成员,而不是通过类型转换。


下面再说说dynamic_cast。这个函数效率很低,能不用就不用吧。通常,只有当你想在一个认定为派生类对象的身上执行派生类操作,而你却只有一个基类指针或者引用时,才需要dynamic_cast:

class Base
{
public:
	Base(int i = 0):bVal(i){cout<<"基类构造函数"<<endl;}
/*	virtual void say()
	{
		
		cout<<"基类函数"<<endl;
	}*/
	virtual void fun(){}
private:
	int bVal;
};



class Drived:public Base
{
public:
	Drived(int i = 10, int j = 5):Base(i),dVal(j){cout<<"派生类构造函数"<<endl;}
	void say()
	{
		cout<<"派生函数"<<endl;
	}
private:
	int dVal;
};
int main()
{
	Base *pd = new Drived;
	Drived* pd1 = dynamic_cast<Drived*>(pd);
	pd1->say();
	return 0;
}

注意,这个转化这里要求基类必须函有虚函数。
通常,有两种办法解决这个问题:
1.在基类里添加对应的虚函数:
public:
	Base(int i = 0):bVal(i){cout<<"基类构造函数"<<endl;}
	virtual void say(){}//什么也不做

然后就可以通过动态绑定来调用派生类的函数了:

	Base *pd = new Drived;
	pd->say();

2.使用容器存储只想直接指向派生类的对象的指针(通常是智能指针)。这便消除了通过基类接口处理对象的需要,假设继承派生体系中只有派生类才有say函数,那么

	typedef vector<std::tr1::shared_ptr<Drived>> VPD;
	VPD d(1);
	for(VPD::iterator iter = d.begin(); iter != d.end();++iter)
		(*iter)->say();

最后,避免连续使用dynamic_cast。因为这样效率很低,而且基础不稳定。


总而言之,好的代码很少使用类型转化。如果非要类型转换,则应该使用C++风格的,且应该把它隐藏在某个函数中,而不是暴漏给用户。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值