Effective C++ 读书笔记(五)

5 实现

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

尽可能延后变量的定义,直到非得使用该变量的前一刻为止,甚至应该尝试延后这份定义直到能够给它初值实参为止。如果这样,不仅能够避免构造和析构非必要对象,还可以避免无意义的default 构造行为。

循环怎么办??

//定义于循环外

Widget w;

for (int i = 0; i < n; ++i){

w = …;

}

1个构造函数,1个析构函数,n个赋值操作

//定义于循环内

for (int i = 0; i < n; ++i){

Widget w;

}

 

n个构造函数,n个析构函数

具体取决于一个赋值成本和一组构造+析构成本的大小。

 

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

旧式转型

C风格:(T) expression

函数风格:T(expression)

新式转型

n  const_cast<T>( expression): 移除对象的常量性

n  dynamic_cast<T>(expression): 安全向下转型

dynamic_cast can be used only with pointers and references to objects. Its purpose is to ensure that the result of the type conversion is a valid complete object of the requested class.

Therefore, dynamic_cast is always successful when we cast a class to one of its base classes:

1
2
3
4
5
6
7
8

class CBase { };

class CDerived: public CBase { };

 

CBase b; CBase* pb;

CDerived d; CDerived* pd;

 

pb = dynamic_cast<CBase*>(&d);     // ok: derived-to-base

pd = dynamic_cast<CDerived*>(&b);  // base-to-derivedpb=null

The second conversion in this piece of code would produce a compilation error since base-to-derived conversions are not allowed with dynamic_cast unless the base class is polymorphic.

 

n  reinterpret_cast<T>(expression): 低级转型,结果取决于编译器,不可移植

n  static_cast<T>(expression): 强迫隐式转换。如non-const到const,int到double,void*到typed*.

static_cast can perform conversions between pointers to related classes, not only from the derived class to its base, but also from a base class to its derived.

 

一个复杂的转型操作:

class Window{

public:

         Window():count(0){

                   cout<<"Window constructor"<<endl;

         }

         virtual void onSize(){

                   cout<<"Window::onSize()"<<endl;

                   count = 1;

         }

         int count;

};

 

class SpecialWindow:public Window

{

public:

         SpecialWindow(){cout<<"SpecialWindow constructor"<<endl;}

         virtual void onSize(){                           cout<<"SpecialWindow::onSize()"<<endl;

         Window::onSize();

         cout<<"count = "<<count<<endl;

         }

};

class SpecialWindow:public Window

{

public:

         SpecialWindow(){cout<<"SpecialWindow constructor"<<endl;}

         virtual void onSize()

         {                          cout<<"SpecialWindow::onSize()"<<endl;

         static_cast<Window>(*this).onSize();

         cout<<"count = "<<count<<endl;

         }

};

Output:

Window constructor

SpecialWindow constructor

SpecialWindow::onSize()

Window::onSize()

count = 1

Output:

Window constructor

SpecialWindow constructor

SpecialWindow::onSize()

Window::onSize()

count = 0

第2份程序将*this转型为Window,对函数onSize的调用也因此调用了Window::onSize。但调用的不是当前对象上的函数,而是稍早转型动作所建立的一样*this对象之base class成分的暂时副本上的onSize,这也是为什么count值不相等的原因。

 

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

class Point{

public: Point(int x, int y);

         …

         void setX(int newVal);

         void setY(int newVal);

};

 

struct RectData

{

Point ulhc;

Point lrhc;

}

class Rectangle

{

private:

std::tr1::shared_ptr<RectData> pData;

}

class Rectangle

{

public:

Point& upperLeft()const {return pData->ulhc;}

Point& lowerRight()const {return pData->lrhc;}

}

Point c1(0, 0), c2(100,100);

const Rectangle rec(c1, c2);

rec.upperLeft( ).setX(50); //rec显然不应该是可变的

class Rectangle

{

public:

const Point& upperLeft()const {return pData->ulhc;}

const Point& lowerRight()const {return pData->lrhc;}

}

class GUIObject{ };

const Rectangle boundingBox(const GUIObject& obj);

GUIObject* pgo;

const Point* pUpperLeft =

&(boundingBox(*pgo).upperLeft());

可能出现指针空悬,虚吊

       

 

条款29:为“异常安全”而努力是值得的

异常安全函数提供以下三个保证之一:

n   基本承诺:如果异常被抛出,程序内的任何事物仍然保持在有效状态下。没有任何对象或数据结构会因此而败坏,所有对象都处于一种内部前后一致的状态。

n   强烈保证:如果异常被抛出,程序状态不改变。如果函数成功就完全成功,如果失败,程序就回到调用函数之前的状态。

n   不抛掷保证:承诺绝不抛出异常。

 

class PrettyMenu

{

public: void changeBackgroud(std::istream& imgSrc);

private:

         Mutex mutex;

         Image* bgImage;

         int imageChanges;

};

void PrettyMenu ::changeBackgroud(std::istream& imgSrc)

{

lock(&mutex);

delete bgImage;

++imageChanges;

bgImage = new Image(imgSrc);

unlock(&mutex);

}

替代方案1:

class PrettyMutex

{

std::tr1::shared_ptr<Image> bgImage;

};

void PrettyMenu ::changeBackgroud(std::istream& imgSrc)

{

Lock(&mutex);

bgImage.reset( new Image(imgSrc)); //不需要delete旧图像,这个动作已经由智能指针处理掉了

++imageChanges;

}

替代方案2: copy-and-swap技术

struct PMImpl{

std::tr1::shared_ptr<Image> bgImage;

int imageChanges;

};

class PrettyMenu

{

private: Mutex mutex;

         std::tr1::shared_ptr<PMImpl> pImpl;

};

void PrettyMenu ::changeBackgroud(std::istream& imgSrc)

{

Lock(&mutex);

std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl));

pNew->bImage.reset( new Image(imgSrc)); //不需要delete旧图像,这个动作已经由智能指针处理掉了

++pNew->imageChanges;

swap(pImpl, pNew);

}

 

条款30:透彻了解inline的里里外外

inline函数:看起来像函数,动作像函数,比宏好很多,可以调用它们又不需蒙受函数调用所招致的额外开销。

inline会引发代码膨胀,编译器拒绝太过复杂的函数inlining(带有循环或递归),而所有对virtual函数的调用(除非是最平淡无奇的)也都会使inlining落空。

如果某程序要取某个inline函数的地址,编译器通常必须为此函数生成一个outlined函数本体。这意味对inline函数的调用有可能被inlined,也可能不被inlined。

inline void f () {…}

void (*pf) ( ) = f;

f(); //内联

pf(); //不被内联

         inline函数无法随着程序库的升级而升级,一旦程序库设计者决定改变f,所有用到f的客户羰程序都必须重新编译,而如果f是non-inline函数,一旦它有任何修改,客户端程序都只需要重新连接就好。

 

条款31:将文件的编译依存关系降至最低

n   支持编译依存性最小化的一般构想是:相依申明式,不要依于定义式。基于此构想的两个手段是Handle classes和Interface classes。

n   程序库头文件应该以完全且仅有声明式的形式存在,这种做法不论是否涉及template都适用。

 

posted on 2012-11-21 22:40  ArcherXu  阅读(202)  评论(0编辑  收藏  举报

导航