条款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(新式)转型,不要使用旧式转型。前者很容易辨认出来,而且也有着比较分门别类的职责。

条款27:尽量少做转型动作,布布扣,bubuko.com

时间: 2024-10-05 20:42:18

条款27:尽量少做转型动作的相关文章

Effective C++ 阅读笔记_条款27 尽量少做转型动作

Effective C++ 阅读笔记_条款27 尽量少做转型动作 1.转型的三种形式,可以分为两大类. (1)旧式转型(old-style casts) (1.1) (T) expresstion (1.2) T (expression) (2) 新式转型(c++-style casts) (2.1)const_cast<T> (expression) (2.2)dynamic_cast<T> (expression) (2.3)reinterpret_cast<T>

Effective C++ 条款27 尽量少做转型操作

1. 转型语法有三种, C风格转型:(T)expression 函数风格转型:T(expression) 两种方式除了小括号位置不同没有差别,可以称为"旧式转型". C++提供四种新式转型: const_cast<T>(expression): 将const变量引用或指针转为非const引用或指针,移除变量的常量特性.T必须是指针或引用. 注:虽然经const_cast转型后的变量可以被更改,但由于"常量折叠"(c++编译器在编译时将const 变量替换

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

(一)C风格旧式转型: (T)expression T(expression) (二)C++提供四种新式转型: (1)const_cast<T>(expression):去除表达式的常量性,是C++中唯一能做此操作的转型操作符. (2)dynamic_cast<T>(expression):主要用来执行"安全向下转型",即用来决定某对象是否归属继承体系中的某个类型.它是唯一无法由旧式语法执行的动作,也是唯一可能耗费重大运行成本的转型动作. (3)reinterp

条款27:尽量少做转型动作(Minimize casting)

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

[Effective C++ --027]尽量少做转型动作

引言                                                                                                                                                                                                C风格的转型动作 (T)expression // 将expression转型为T 函数风格的转型动作看起来像这

Effective C++ Item 27 少做转型操作

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie todo Item34 旧式转型 (T) expression 或 T (expression) 新式转型 const_cast<T>(expression) 通常被用来将对象的常量性转除(cast away the constness) dynamic_cast<T>(expression) 执行"安全向下转型",也就是用来决定某对象是否归属继承体系中的

Effective C++:规定27:尽量少做动作的过渡

(一个)C风格遗留转换: (T)expression T(expression) (二)C++提供四种新式转型: (1)const_cast<T>(expression):去除表达式的常量性.是C++中唯一能做此操作的转型操作符. (2)dynamic_cast<T>(expression):主要用来运行"安全向下转型",即用来决定某对象是否归属继承体系中的某个类型.它是唯一无法由旧式语法运行的动作,也是唯一可能耗费重大运行成本的转型动作. (3)reinter

《Effective C++》学习笔记——条款27

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 五.Implementations Rule 27:Minimize casting 规则 27:尽量少做转型动作 1.一些基础 C++规则的设计目标之一 -- 保证"类型错误"绝对不可能发生. 理论上,如果程序很"干净地"通过编译

Effective C++ 条款27

尽量少做转型动作 尽量少做转型动作有什么目的?非常明显无非就是提高程序的稳定性.提高程序的运行效率. 那么.有哪些转型方式?每种方式都有什么弱点? 这是我们本节学习的重点. C++有四种转型: const_cast<T>(expression) dynamic_cast<T>(expression) reinterpret_cast<T>(expression) static_cast<T>(expression) 每种转型的作用例如以下: 1.const_