码迷,mamicode.com
首页 > 其他好文 > 详细

EC读书笔记系列之14:条款26、27、28、29

时间:2015-11-10 09:17:01      阅读:198      评论:0      收藏:0      [点我收藏+]

标签:

条款26 尽可能延后变量定义式的出现时间(Lazy evaluation

记住:

★尽可能延后变量定义式的出现。这样做可增加程序的清晰度并改善程序效率

----------------------------------------------------------------------

举例说明:

std::string encryptPassword( const std::string &password ) {
    
    using namespace std;
    string encrypted;  //过早定义变量“encrypted”
    if( password.length() < MininumLength ) {
    
        throw logic_error( "Password is too short" );
    }
    ...
    return encrypted;
}

一旦if语句中异常抛出,则上面的encrypted变量则未被使用,但仍得付出该变量的构造和析构成本,∴最好延后encrypted的定义式直到确实需要它。

 

更有甚,不只应延后变量的定义,甚至还应延后这份定义直到能给它初值为止,定义和赋初值一并完成,避免无谓的 先default构造 再赋初值。

 

对于循环的情况:

//方法A:定义于循环外
Widget w;
for( int i=0; i<n; i++ ) {

    w = 取决于i的某个值;
    ...
}

//方法B:定义于循环内
for( int i=0; i<n; i++ ) {

    Widget w(取决于i的某个值);
    ...
}

方法A 的成本:1个constructor + 1个destructor + n个assignment operate

方法B的成本:n个constructor + n个destructor

 

若赋值成本低于一组构造+析构成本,这样A大体而言交高效,否则B好。另外A造成w的作用域(覆盖整个循环)比B更大,有时那对程序的可理解性和易维护性造成冲突。

 

条款27 尽量少做转型动作

记住:

★若可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_cast。若有个设计需要转型动作,试着发展无需转型的替代方案

★若转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需将转型放进他们自己的代码内

★宁可使用C++风格转型(新式转型),不要使用旧式转型。前者很容易辨识出来,而且也比较有着分门别类的职掌

---------------------------------------------------------------------------------------------------

两种旧式转型语法:

  (T)expression

  T(expression) //函数风格的转型动作

 

C++的四种新式转型:

  const_cast<T>( expression ); //消除对象的常量性

  dynamic_cast<T>( expression ); //安全向下转型(即向派生类端)

  reinterpret_cast<T>( expression ); //执行低级转型

  static_cast<T>( expression ); //强迫隐式转换

 

“我”(书作者)唯一使用旧式转型的情况是:当我要调用一个explicit构造函数(不允许隐式转换)将一个对象传递给一个函数时

class Widget {

    public:
        explicit Widget( int size ); //ctor声明为explicit就不会导致隐式转换!!!
        ...
};

void doSomeWork( const Widget &w );
doSomeWork( Widget(15) );  //以一个int加上“函数风格”的转型动作创建一个Widget

不要错误地认为转型其实什么都没做,只是告诉编译器把某个类型视为另一种类型。任何一个类型转化往往真的令编译器编译出运行期间执行的码。

------------------

 关于转型的一个错误理解的例子:

假设有个Window基类和SpecialWindow派生类,进一步假设SpecialWindow的onResize被要求首先调用Window的onResize:

class Window {

    public:
        virtual void onResize() {...}
        ...
};

class SpecialWindow : public Window {

    public:
        virtual void onResize() {
        
            static_cast<Window>(*this).onResize();
            ... //这里进行SpecialWindow专属行为
        }
        ...
};

其实这句static_cast<Window>(*this).onResize();并非在当前对象身上调用Window::onResize之后又在该对象身上执行SpecialWindow专属动作。他是在“当前对象之base class成分”的副本上(!!!)调用Window::onResize,然后在当前对象身上执行SpecialWindow专属动作。这很有可能使当前对象出现“伤残”状态:其base class成分的更改没有落实,而derived class成分的更改倒是落实了。

 

解决方案如下:避免使用转换

class SpecialWindow : public Window {

    public:
        virtual void onResize() {
        
            Window::onResize(); 
            ...
        }
        ...
};

-----------------

除了对一般转型保持机敏与猜疑,更应该在注重效率的代码中对dynamic_cast保持机敏与猜疑。∵dynamic_cast的许多实现版本执行速度相当慢!!!

 

-----------------

之所以需dynamic_cast,通常是因为想在一个derived class对象身上执行derived class函数,但手上仅一个指向base的pointer或reference,有两个一般性做法:

做法一:使用容器并在其中存储直接指向derived class对象的指针(通常是smart pointer)

假设先前的Window/SpecialWindow继承体系中仅SpecialWindow才支持闪烁效果:

不要这样做:
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 != winPtrs.end(); iter++ ) {
    
    if( SpecialWindow *psw = dynamic_cast<SpecialWindow*>( iter->get() )  ) {
    
        psw->blink();
    }
}
而应这样做:
typedef std::vector< std::tr1::shared_ptr<SpecialWindow> > VPSW;
VPSW winPtrs;
...
for( VPSW::iterator iter=winPtrs.begin(); iter != winPtrs.end(); iter++ ) {
    
    (*iter)->blink(); //这样写比较好,∵摆脱了dynamic_cast
}

做法二:在base class内提供virtual函数做你想对各个Window派生类做的事:

class Window {

    public:
        virtual void blink() {} //缺省实现代码,啥也没做
                //条款34将说明缺省实现代码可能是个馊主意
};

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 != winPtrs.end(); iter++ ) {
    
    (*iter)->blink(); //也摆脱了dynamic_cast
}

 

-------------------------------------------

绝对要避免“连串dynamic_cast”

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()) ) {...}
    else if( SpecialWindow2 *psw2 = dynamic_cast<SpecialWindow2*>(iter->get()) ) {...}
    else if( SpecialWindow3 *psw3 = dynamic_cast<SpecialWindow3*>(iter->get()) ) {...}
    ...
}

这样产生出来的代码又大又慢,且基础不稳。

 

条款28 避免返回handles指向对象内部成分

记住:

★避免返回handles(包括reference,指针,迭代器)指向对象内部。遵守这个条款可增加封装性;帮助const成员函数的行为像个const;并将发生“虚吊号码牌”的可能性降到最低。

-------------------------------------------------------------------------

 

 

EC读书笔记系列之14:条款26、27、28、29

标签:

原文地址:http://www.cnblogs.com/hansonwang99/p/4951854.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!