条款26 尽可能延后变量定义式的出现时间 条款27 尽可能的少做转型动作
道理很好理解,防止定义了,却因为return或者异常没有被使用,浪费资源。
C++的四种转型
const_cast<T>(expression) dynamic_cast<T>(expression) reinterpret_cast<T>(expression) static_cast<T>(expression)去掉常量属性:
int val = 6; const int *pCVal = &val; int *pVal = const_cast<int*>(pCVal); cout <<"val :"<<val<<" pCVal:"<<*pCVal<<" pVal:"<<*pVal<<endl; *pVal = 10; //修改指针 val也会修改 cout <<"val :"<<val<<" pCVal:"<<*pCVal<<" pVal:"<<*pVal<<endl; //打印结果 val :6 pCVal:6 pVal:6 val :10 pCVal:10 pVal:10但这种常量性的转换只是针对于指针或引用(包括this指针),是不能针对于普通变量的,比如下面的做法就是错误的:
const int val = 3; //编译出错,只能使用static_cast或者C风格转换 int val_temp = const_cast<int>(val);有个问题 对于const int 的类型:
const int val = 6; int *pVal = const_cast<int*>(&val); cout <<"val :"<<val<< " addr:"<<&val<<" pVal:"<<*pVal<<" addr:"<<pVal<<endl; *pVal = 10; cout <<"val :"<<val<< " addr:"<<&val<<" pVal:"<<*pVal<<" addr:"<<pVal<<endl; //打印结果 val :6 addr:0x28fea8 pVal:6 addr:0x28fea8 val :6 addr:0x28fea8 pVal:10 addr:0x28fea8不是很懂这个怎么解释但是奇怪的是调试器里显示val的值已经是10了。
常规用法就不提了,但是有一个可能会犯的错误! 不清楚原理的转型会带来严重的bug,比如书上提到的,假设有一个基类Window,它有一个派生类SpecialWindow,它们都有一个OnResize()的成员函数,在派生类的OnResize()中想要先调用基类的OnResize(),于是有程序员便这样做了:
void SpecialWindow::OnResize() { static_cast<Window>(*this).OnResize(); … }这样真的没有问题吗?将派生类对象转成Window对象,然后对其调用OnResize(),这确确实实调用的是基类的OnResize(),但OnReszie()对成员变量操作的结果是你想要的吗?
答案是否定的,因为static_cast生成的是一个临时的基类的对象,这个对象并不是真正组成派生类的那个基类,而是它的一个副本(这个副本的成员变量值与派生类对象中基类的成分是一样的,但地址不同),调用OnResize()变更的结果是这个副本的成员变量变了,但派生类中包含的基类的成员变量却是没有任何变化的。好了,这就是bug了,在之后的测试中这个问题会让程序员纠结好一阵子。
一句话,转型生成的是一个copy,一份副本,有的时候这并不是你想要的,修正方法其实很简单,就是:
void SpecialWindow::OnResize() { Window::OnResize(); … }有的转型操作也是比较废的,比如dynamic_cast,这个转型会对类名称进行strcmp,以判断向下转型是否合理,如果继承深度比较大,那么每一次的dynamic_cast将会进行多次strcmp,这将严重影响程序的执行效率。解决方法就是如果可以话,直接使用指向子类的指针(保存下来),真的想用父类的指针(比如工厂设计模式等),那就考虑多态吧,在父类相应的函数前面加virtual,然后子类进行覆盖即可。
把指针转成整数或者把整数转成指针,平台依赖性很强,不建议使用。
但是可以试试:
int val = 5; int *pVal = &val; int rval = reinterpret_cast<int>(pVal); //指针强转成int cout <<"val:"<<&val<<" pVal:"<<pVal<<" rval:"<<hex<<rval<<endl; //int 转换成指针 相当于把地址转换成指针 int* pSVal = reinterpret_cast<int*>(rval); cout <<"pSVal:"<<pSVal<<" val:"<<*pSVal<<endl; //打印结果 val:0x28fea0 pVal:0x28fea0 rval:28fea0 pSVal:0x28fea0 val:5如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts。
如果转型是必要的,试着将它隐藏于某个函数背后,客户可以随后调用这个函数,而不需要将转型放在他们自己的代码里。
宁可使用C++风格的新式转型,少用C风格转型,因为前者很容易辨识出来,而且也比较有着分门别类的职掌。