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:尽量少做转型动作
- 尽量避免转型,特别是dynamic_casts,使用无转型设计代替有转型设计
- 一定要转型时,试着将其影藏于函数背后
- 尽可能使用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);//置换数据
}