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

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

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

(一个)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辨识出来,并且也比較有着分门别类的职掌。





版权声明:本文博客原创文章,博客,未经同意,不得转载。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/117445.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Android端 WebP图片压缩与传输的一点探索

    Android端 WebP图片压缩与传输的一点探索简介直到4g时代,流量依然是宝贵的东西。而移动网络传输中,最占流量的一种载体:图片,成为了我们移动开发者不得不关注的一个问题。我们关注的问题,无非是图片体积和质量如何达到一个比较和谐的平衡,希望得到质量不错的图片同时体积还不能太大。走在时代前列的谷歌给出了一个不错的答案——WebP。WebP是一种图片文件格式,在相同的压缩指标下,webp的有损压缩能比jpg小25-34%。而在

    2025年5月25日
    1
  • ReLU激活函数:简单之美

    ReLU激活函数:简单之美导语在深度神经网络中,通常使用一种叫修正线性单元(Rectifiedlinearunit,ReLU)作为神经元的激活函数。

    2022年6月15日
    26
  • GFS – The Google File System

    GFS – The Google File SystemTheGoogleFileSystemhttp://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.125.789&amp;rep=rep1&amp;type=pdfhttp://www.dbthink.com/?p=501,中文翻译 Google牛人云集的地方,但在设计系统时,却非常务实,没有采用什么复杂和时髦…

    2022年6月1日
    30
  • IntelliJ IDEA 如何配置git[通俗易懂]

    提示:IntelliJIDEA以下简称IDEA;IntelliJIDEA配置git:需要的材料:一、git.exe二、配置git.exe的环境变量三、IDEA编辑器接下来就是配置了:①你必须去github官网下载一个git.exe,不知道官网的请点击这里,下载完之后安装即可。②安装完之后打开所在目录找到bin这个文件夹,我的是在D:\软件\GitHub\G…

    2022年4月9日
    73
  • AOP【面向切面编程】「建议收藏」

    AOP【面向切面编程】「建议收藏」文章目录AOP介绍AOP术语AOP的实现方式AOP介绍AOP(AspectOrientedProgramming),即面向切面编程,可以说是OOP(ObjectOrientedProgramming),面向对象编程的补充和完善。  面向切面编程是面向对象中的一种方式而已。在代码执行过程中,动态嵌入其他代码,叫做面向切面编程。常见的使用场景:日志事物数据库操作面向切面编程,…

    2025年7月23日
    4
  • c语言控制输出格式-小数点位数

    c语言控制输出格式-小数点位数控制小数位数就是通过输出格式说明符来规定的printf(%m.nf)表示打印至少m个字符宽度(包括整数、小数点和小数部分的位数),n位小数1.printf(“%3.0f”,floatNum):不保留小数说明:%3.0f表明待打印的浮点数(floatNum)至少占3个字符宽,且不带小数点和小数部分,整数部分至少占3个位宽;注意:这里的3只代表整数部分至少占3位,舍弃小数点和小数点…

    2022年7月24日
    44

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号