Effective C++笔记(一)——条款26-29

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


为何要尽量延后?

当程序中途跳出而导致变量未被使用,但是必须进行构造和析构。


最佳初始化变量

直接在构造时指定初值比构造之后再赋值效率高(条款4)

   ...
   std::string encrypted(password);
   ...

循环内变量定义在循环内还是循环外?

程序A:定义于循环外

   //方法A:循环外定义
   POINT point;
   for (int i = 0; i < 1000000000; i++)
   {
	point = tmp;
	//tmp = point;
   }

程序B:定义于循环内

   //方法B:循环内定义
   for (int i = 0; i < 1000000000; i++)
   {
	POINT point(tmp);
	//tmp = point;
   }

测试程序

   int PostponeVariable()
   {
	POINT tmp;
	tmp.x = 12;
	tmp.y = 13;

	clock_t start = clock();
	//方法A:循环外定义
	POINT point;
	for (int i = 0; i < 1000000000; i++)
	{
		point = tmp;
		//tmp = point;
	}
	clock_t end = clock();
	printf("A:%ld\n", (end - start));

	start = clock();
	//方法B:循环内定义
	for (int i = 0; i < 1000000000; i++)
	{
		POINT point(tmp);
		//tmp = point;
	}
	end = clock();
	printf("B:%ld\n", (end - start));

	return 0;
   }

结果

A:2953
B:2774
请按任意键继续. . .

全部都是A比B执行的快,之前一直自以为是的觉得放在循环外效率比较高!


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

  1. 尽量避免转型,特别是dynamic_casts,使用无转型设计代替有转型设计
  2. 一定要转型时,试着将其影藏于函数背后
  3. 尽可能使用C++新式转型

条款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{ 
    ... 
    Point& upperLeft()const {return pData->ulhc;} 
    Point& lowerRight()const {return pData->lrhc;} 
private: 
    std::tr1::shared_ptr<RectData> pData; 
};

const对象被修改:

Point coord1(0,0); 
Point coord2(100,100); 
const Rectangle rec(coord1, coord2); 
rec.upperLeft().setX(50);//现在rec变成从(50,0)到(100,100)

修正:

class Rectangle{ 
    ... 
    const Point& upperLeft()const {return pData->ulhc;} 
    const Point& lowerRight()const {return pData->lrhc;} 
private: 
    std::tr1::shared_ptr<RectData> pData; 
};

避免返回指向对象内部部件的句柄(引用、指针或迭代器)。这样做可以增强封装性,帮助 const 成员函数拥有更加“ const ”的行为,并且使“野句柄”出现的几率降至最低。


条款29:为“异常安全”而努力

1. 修改菜单背景

class PrettyMenu{ 
public: 
    ... 
    void changeBackground(std::istream& imgSrc); 
    ... 
private: 
    Mutex mutex; 
    Image* bgImage; 
    int imageChanges; 
}; 
void PrettyMenu::changeBackground(std::istream& imgSrc) 
{ 
    lock(&mutex); 
    delete bgImage; 
    ++imageChanges; 
    bgImage = new Image(imgSrc); 
    unlock(&mutex); 
}

2. 异常安全条件

  • 不泄漏任何资源:若new Image(imgSrc)异常,则unlock永不解锁
  • 不允许数据破坏:若new Image(imgSrc)异常,则bgImage指向被删除的指针,且imageChanges已被修改,导致未成功执行却修改了数据。

3. 对象管理资源:解决资源泄漏

void PrettyMenu::changeBackground(std::istream& imgSrc) 
{ 
    Lock ml(&mutex);//来自条款14; 
    delete bgImage; 
    ++imageChanges; 
    bgImage = new Image(imgSrc); 
}

4. 异常安全函数保证

  • 基本承诺:异常抛出时,保持有效状态,没有对象或数据结构被破坏
  • 强烈保证:异常出现时,程序状态不改变,成功则完全成功,失败则会到之前状态
  • 不抛掷(nothrow)保证:承诺不抛出异常

5. 基本承诺

class PrettyMenu{ 
    ... 
    std::tr1::shared_ptr<Image> bgImage; 
    ... 
};

void PrettyMenu::changeBackground(std::istream& imgSrc) 
{ 
    Lock ml(&mutex); 
    bgImage.reset(new Image(imgSrc)); 
    ++imageChanges; 
}

6. 强烈保证: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::changeBackground(std::istream& imgSrc) 
{ 
    using std::swap; 
    Lock ml(&mutex); 
    std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl)); 
    pNew->bgImage.reset(new Image(imgSrc)); //修改副本 
    ++pNew->imageChanges; 
    swap(pImpl, pNew);//置换数据 
}
posted @ 2014-10-23 11:58  Sky_Watcher  阅读(251)  评论(0编辑  收藏  举报