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

时间:2024-01-07 15:07:44

条款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);//置换数据
}