首页 > 代码库 > 条款27:尽量少做转型动作

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

条款27:尽量少做转型动作
C++的四种显示类型转换
①reinterpret_cast(在编译期间实现转换)
      将一个类型的指针转换成另一个类型的指针。这种转换不用于修改指针变量值数据存放的格式(不改变指针变量值),只需在编译期间重新解释指针的类型就可以做到,可以将指针值转换为一个整型数。但是不能用于非指针类型的转换,否则将不会通过编译。
     意图执行低级转型,结果取决于编译器,故不可移植。
②const_cast(在编译期间实现转换)
       用于去除指针变量的常量属性,将它转换为一个对应指针类型的普通变量,反过来,也可以将一个非常量的指针变量转换为常量指针变量
③stactic_cast(在编译期间实现转换)
      用来强迫隐式转换,例如将int转换为double,将void*转换为typed指针,将non-const对象转换为const对象。
④dynamic_cast(在运行期间实现转换,并可以返回转换成功与否的标志)
       用于继承体系下的"向下安全转换",通常用于将基类对象指针转换为其子类对象指针,它也是唯一一种无法用旧式转换进行替换的转型,也是唯一可能耗费重大运行成本的转型动作.
       运算符dynamic_cast可以针对两种数据类型做强制转换:指针类型和引用类型
转型在对象中运用可能会导致错误的调用
class Window
{
public:
	Window(const int size) : _size(size) {}
	virtual ~Window() {}
	virtual void onResize(const int size) {_size = size;}
	int GetSize() const {return _size;}
private:
	int _size;
};

class GlassWinsow : public Window
{
public:
	GlassWinsow(const int size, const int gSize) : Window(size), _gSize(gSize) {}
	void onResize(const int size, const int gSize)
	{
		static_cast<Window>(*this).onResize(size);
		_gSize = gSize;
	}
	int GetGlassSize() const {return _gSize;}
private:
	int _gSize;
};
调用
	GlassWinsow gw(2, 3);
	cout<<"Window size:"<<gw.GetSize()<<" GlassWindow size:"<<gw.GetGlassSize()<<endl;
	gw.onResize(4, 5);
	cout<<"Window size:"<<gw.GetSize()<<" GlassWindow size:"<<gw.GetGlassSize()<<endl;
结果
Window size:2 GlassWindow size:3
Window size:2 GlassWindow size:5
        将*this转型为window调用的并不是当前对象上的函数,而是稍早转型动作所建立的一个“*this对象的base class成分”的暂时副本身上的onResize!并不是在当前对象身上调用Window::onResize之后又在该对象上执行SpecialWindow专属行为。不,它是在“当前对象之base calss成分”的副本上调用Window::onResize,然后在当前对象上执行SpecialWindow专属动作。如果Window::onResize修改了对象内容,当前对象其实没被改动,改动的是副本。然而SpecialWindow::onResize内如果也修改对象,当前对象真的会被改动。这使当前对象进入一种“伤残”状态:其base class成分的更改没有落实,而derived class成分的更改倒是落实了。
       解决之道是拿掉转型动作,代之你真正想要说的话。所以,真正的解决方法是:
修改后
	void onResize(const int size, const int gSize)
	{
		Window::onResize(size);
		_gSize = gSize;
	}
同样调用结果
Window size:2 GlassWindow size:3
Window size:4 GlassWindow size:5
dynamic_cast
        dynamic_cast的许多实现版本执行速度相当慢。假如至少有一个很普通的实现版本基于“class名称之字符串比较”,如果你在四层深的单继承体系内的某个对象身上执行dynamic_cast,可能会耗用多达四次的strcmp调用,用以比较class名称。深度继承或多重继承的成本更高!某些实现版本这样做有其原因(它们必须支持动态链接)。在对注重效率的代码中更应该对dynamic_cast保持机敏猜疑。
       之所以需要用dynamic_cast,通常是因为你想在一个你认定为derived class对象身上执行derived class操作函数,但你的手上只有一个“指向base”的pointer或者reference,你只能靠他们来处理对象。两个一般性做法可以避免这个问题:
①使用容器,并在其中存储直接指向derived class对象的指针(通常是智能指针),如此便消除了“通过base class接口处理对象”的需要。假设先前的Window/GlassWindow 继承体系中有GlassWindow 才支持闪烁效果,试着不要这样做:
在类GlassWindow 中添加闪烁效果函数
void blink() {cout<<"GlassWindows Link\n";}
这样调用
	typedef std::vector<std::tr1::shared_ptr<Window>> VPW; 
	VPW winPtrs; 
	winPtrs.push_back(std::tr1::shared_ptr<Window>(new GlassWinsow(2, 3)));
	for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter) 
	{ 
		if(GlassWinsow* psw = dynamic_cast<GlassWinsow*>(iter->get())) 
			psw->blink(); 
	}
我们应该改成下面的调用方式
	typedef std::vector<std::tr1::shared_ptr<GlassWinsow>> VPSW; 
	VPSW winPtrs; 
	winPtrs.push_back(std::tr1::shared_ptr<GlassWinsow>(new GlassWinsow(2, 3)));
	for (VPSW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter) 
	{ 
		(*iter)->blink();
	}
        这种做法无法在同一个容器内存储指针“指向所有可能之各种Window派生类”。在Window上继承,那么就要定义多个类型的容器
 ②通过base class接口处理“所有可能之各种window派生类”,那就是在base class 里提供virtual函数做你想对各个Window派生类做的事。
修改后代码  
class Window
{
public:
	Window(const int size) : _size(size) {}
	virtual ~Window() {}
	virtual void onResize(const int size) {_size = size;}
	int GetSize() const {return _size;}
	virtual void blink() {}
private:
	int _size;
};

class GlassWinsow : public Window
{
public:
	GlassWinsow(const int size, const int gSize) : Window(size), _gSize(gSize) {}
	void onResize(const int size, const int gSize)
	{
		Window::onResize(size);
		_gSize = gSize;
	}
	int GetGlassSize() const {return _gSize;}

	void blink() {cout<<"GlassWindows Link\n";}
private:
	int _gSize;
};

class WoodWindow : public Window
{
public:
	WoodWindow(const int size, const int gSize) : Window(size), _gSize(gSize) {}
	void onResize(const int size, const int gSize)
	{
		Window::onResize(size);
		_gSize = gSize;
	}
	int GetGlassSize() const {return _gSize;}

	void blink() {cout<<"WoodWindow Link\n";}
private:
	int _gSize;
};
调用
	typedef std::vector<std::tr1::shared_ptr<Window>> VPSW; 
	VPSW winPtrs; 
	winPtrs.push_back(std::tr1::shared_ptr<Window>(new GlassWinsow(2, 3)));
	winPtrs.push_back(std::tr1::shared_ptr<Window>(new WoodWindow(4, 5)));
	for (VPSW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter) 
	{ 
		(*iter)->blink();
	}
GlassWindows Link
WoodWindow Link


绝对必须拒绝的是所谓的“连串(cascading)dynamic_casts,这样导致代码又大又慢

记住
1.如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_cast。如果有个设计需要转型动作,试着发展无需转型的替代设计。
2.如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需要将转型放进他们自己的代码内。
3.宁可使用C++ style(新式)转型,不要使用旧式转型。前者很容易辨认出来,而且也有着比较分门别类的职责。