Effective C++:规定27:尽量少做动作的过渡
(一个)C风格遗留转换:
(T)expression
T(expression)
(二)C++提供四种新式转型:
(1)const_cast<T>(expression):去除表达式的常量性。是C++中唯一能做此操作的转型操作符。
(2)dynamic_cast<T>(expression):主要用来运行“安全向下转型”,即用来决定某对象是否归属继承体系中的某个类型。它是唯一无法由旧式语法运行的动作,也是唯一可能耗费重大运行成本的转型动作。
(3)reinterpret_cast<T>(expression):试图运行低级转型(比如将一个指向整数变量的指针转换为整数),实际动作及结果可能与编译器相关,因而减少了可移植性。
(4)static_cast<T>(expression):隐式转换。注意尽管能够使非const类型转换为const。但反之不能(这个仅仅能由const_cast来完毕)。
(三)新式转型比較受欢迎,原因:
1,非常easy在代码中被辨别出来。
2,各转型动作的目标愈窄化。编译器愈可能诊断出错误的运用。
(四)唯一使用旧式转型的时机是当调用一个explicit构造函数将一个对象传递给一个函数时:
class Widget{
public:
explicit Widget(int size);
...
};
void doSomeWork(const Widget &w);
doSomeWork(Widget(15));//函数风格的转型动作创建一个Widget
doSomeWork(static_cast<Widget>(15));//c++风格的转型动作创建一个Widget对象
(五)先看以下这个代码:
class Base{...};
class Derived : public Base{...};
Derived d;
Base* pb = &d;//隐喻的将derived*转换成Base*
在这里表明:单一的对象可能拥有一个以上的地址。这里我们仅仅是建立一个base
class指针指向一个derived class对象,但有时候上述的两个指针值并不同样。这样的情况下会有个偏移量offset在执行期被施行于Derived*指针身上,用于取得正确的Base*指针值。
实际上一旦使用多重继承,这事差点儿一直发生.即使在单一继承中也可能发生.恐怖!为什么会发生这种事情呢?
由于对象的布局方式和它们的地址计算发式随着编译器的不同而不同,这就以为着写出"依据对象怎样布局"而写出的转型代码在某一平台上行得通,在其他平台上则不一定。
(六)
还有一件关于转型的有趣事情是:我们非常easy写出某些似是而非的代码(其它语言中或许是对的)。
比如SpecialWindow的onResize被要求首先调用Window的onResize。以下是看起来对,实际上错:
class Window {
public:
virtual void onResize() {...}
};
class SpecialWinddow : public Window {
public:
virtual void onResize() {
static_cast<Window>(*this).onResize();//将*this转换成Window,然后调用其onResize;这样不行!
}
};
这里会发生什么问题呢?static_cast<Window>(*this)这个转型动作并非如你想象的那样得到当前对象的基类对象部分,事实上编译器为你产生了的是基类对象的副本。
我运行的onResize方法压根就没有运行到基类对象上。而是运行在它的副本上。
可是SpecialWindow的专属onResize却运行在子类对象上。使得这个对象处于一种"伤残状态“。
解决方法:
class SpecialWinddow:public Window{
public:
virtual void onResize(){
Window::onResize();//调用Window::onResize作用于*this身上
...
}
};
(七)
首先。dynamic_cast的运行速度相当慢。
之所以要用dynamic_cast,一般是由于你想在一个你认定为derived
class对象身上运行derived class操作函数。但你的手上仅仅有一个“指向base”的pointer或者reference。你仅仅能靠他们来处理对象。
可是我们应该尽量避免使用dynamic_cast,有两种替代方法!
第一种方法:
使用容器并在当中存储直接指向derived
class对象的指针(一般是智能指针)如此便消除了“通过base class接口处理对象”的须要。
不要像以下这样用dynamic_cast:
class Window{...};
class SpecialWindow:public Window {
public:
void blink();
};
typedef std::vector<std::tr1::shared_ptr<Window>> VPW;
VPW winPtrs;
for(VPW::iterator iter = winPtrs.begin(); iter != vinPtrs.end(); ++iter) {
if(SpecialWindow* psw = dynamic_cast<SpecialWindow*>(iter->get()))
psw->blink();
}
要改成这样:
typedef std::vector<std::tr1::shared_ptr<SpecialWindow>> VSPW;
VSPW winPtrs;
for(VSPW::iterator iter = winPtrs.begin(); iter != vinPtrs.end(); ++iter) {
(*iter)->blink();
}
另外一种方法:
能够通过base class接口处理“全部可能之各种window派生类”,那就是在base class 里提供virtual函数做你想对各个Window派生类做的事。
class Window {
public:
virtual void blink() { }
};
class SpecialWindow : public Window {
public:
virtual void blink() {...};
};
typedef std::vector<std::tr1::shared_ptr<Window>> VPW;
VPW winPtrs;
for(VPW::iterator iter = winPtrs.begin(); iter != vinPtrs.end(); ++iter) {
(*iter)->blink();
}
(八)
绝对必须拒绝的是所谓的“连串(cascading)dynamic_casts”:
typedef std::vector<std::tr1::shared_ptr<Window>> VPW;
VPW winPtrs;
for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter)
{
if (SpecialWindow1 * psw1 = dynamic_cast<SpecialWindow1*>(iter->get())) {...}
if (SpecialWindow2 * psw1 = dynamic_cast<SpecialWindow2*>(iter->get())) {...}
if (SpecialWindow3 * psw1 = dynamic_cast<SpecialWindow3*>(iter->get())) {...}
}
这种代码应该总是以某些“基于virtual函数调用”的东西取而代之。
优良的c++代码非常少使用转型,我们应该尽可能隔离转型动作。一般是把它隐藏在某个函数内,函数的接口会保护调用者不受函数内部不论什么肮脏龌龊的动作的影响。
请记住:
(1)假设能够,尽量避免转型,特别是在注重下来的代码中避免dynamic_cast.假设有个设计须要转型动作,试着发展无需转型的替代设计。
(2)假设转型是必要的,试着将它隐藏于某个函数背后.客户随后能够调用该函数,而不需将转型放进它们自己的代码内。
(3)宁可使用C++-style(新式)转型,不要使用旧式转型.前者非常easy辨识出来,并且也比較有着分门别类的职掌。
版权声明:本文博客原创文章,博客,未经同意,不得转载。
Effective C++:规定27:尽量少做动作的过渡的更多相关文章
- 读书笔记 effective c++ Item 27 尽量少使用转型(casting)
C++设计的规则是用来保证使类型相关的错误不再可能出现.理论上来说,如果你的程序能够很干净的通过编译,它就不会尝试在任何对象上执行任何不安全或无意义的操作.这个保证很有价值,不要轻易放弃它. 不幸的是 ...
- 读书笔记_Effective_C++_条款二十七:尽量少做转型动作
有关转型的几种做法,已经在早些的博客中写过了.这里先简单回顾一下,再讲一讲effective中对之更深入的阐述. 转型可以按风格可以分成C风格转型和C++风格转型两大类,C风格转型很容易看到,因为我们 ...
- Effective C++ -----条款27:尽量少做转型动作
如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts.如果有个设计需要转型动作,试着发展无需转型的替代设计. 如果转型是必要的,试着将它隐藏于某个函数背后.客户随后可以调用该 ...
- [Effective C++ --027]尽量少做转型动作
引言 ...
- 条款27:尽量少做转型动作(Minimize casting)
NOTE : 1.如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts. 如果有个设计需要转型动作,试着发展无需转型的替代设计. 2.如果转型是必须要的,试着将它隐藏于某个函 ...
- [EffectiveC++]item27:尽量少做转型动作
- Effective C++:条款27——条款
条款27:尽量少做转型动作 单一对象可能拥有一个以上的地址!
- Effective C++ 条款27
尽量少做转型动作 尽量少做转型动作有什么目的?非常明显无非就是提高程序的稳定性.提高程序的运行效率. 那么.有哪些转型方式?每种方式都有什么弱点? 这是我们本节学习的重点. C++有四种转型: con ...
- Effective Objective-C 2.0 — 第二条:类的头文件中尽量少引入其他头文件
第二条:类的头文件中尽量少引入其他头文件 使用向前声明(forward declaring) @class EOCEmployer 1, 将引入头文件的实际尽量延后,只在确有需要时才引入,这样就可以减 ...
随机推荐
- c++ cin>>详解
参考地址:http://www.cnblogs.com/A-Song/archive/2012/01/29/2331204.html 程序的输入都建有一个缓冲区,即输入缓冲区.一次输入过程是这样的,当 ...
- httpcomponents-client-ga(4.5)
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/ Chapter 1. Fundamentals Prev Next ...
- C++基础学习教程(八)
转载请注明出处:http://blog.csdn.net/suool/article/details/38300117 引入 在进行下一步的学习之前,我们须要厘清几个概念. RAII 首先介绍一个编程 ...
- 一个简单而经典的RTX51 Tiny应用实例
关于RTX51 Tiny嵌入式实时操作系统的描写叙述请參考本人的上一篇博文(RTX51 Tiny实时操作系统学习笔记-初识RTX51 Tiny). 本篇博文.我将通过一个实例代码,带大家深入了解一下R ...
- HDU 4126 Genghis Khan the Conqueror MST+树形dp
题意: 给定n个点m条边的无向图. 以下m行给出边和边权 以下Q个询问. Q行每行给出一条边(一定是m条边中的一条) 表示改动边权. (数据保证改动后的边权比原先的边权大) 问:改动后的最小生成树的权 ...
- 谷歌Web中国开发手册:1目的&夹
原版的:https://developers.google.com/web/fundamentals/getting-started/your-first-multi-screen-site/ 该网站 ...
- Android 解决Gallery下ScrollView滑动事件冲突
在Gallery下,里面内容过长超出屏幕,这时我们可以用ScrollView来滚动,但是这样做了以后,会发现一个问题,Gallery的滑动事件和ScrollView的滑动事件起冲突,这时我们可以自定义 ...
- hihocoder第42周 3*N骨牌覆盖(状态dp+矩阵快速幂)
http://hihocoder.com/contest/hiho42/problem/1 给定一个n,问我们3*n的矩阵有多少种覆盖的方法 第41周做的骨牌覆盖是2*n的,状态转移方程是dp[i] ...
- hdu 4524 郑厂长系列故事——逃离迷宫 小水题
郑厂长系列故事——逃离迷宫 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) To ...
- 代写java程序qq:928900200
学校为全面提升学校教学质量,提高管理水平,决定开发一套小型成绩管理系统,实现以下功能.1) 系统用户分为管理员.教师和学生三种角色,每种角色都可以包含若干个用户.其中管理员登录后可以进行教师.学生 ...