- 尽可能的延后变量定义式出现的时间
- 产生问题:定义一个变量,其含有构造函数和析构函数。那么当代码运行到它的时候,就必须会产生构造成本和析构成本,如果这个变量最终未使用,那么就是浪费了资源空间,所以应该尽可能的延后变量定义式的时间
- 解决方法:不只是应该延后变量的定义,知道非得使用该变量的前一刻为止,甚至应该尝试延后这份定义知道能够给他初值实参为止。这样可以避免构造析构函数和默认构造函数
- 对于循环体中的变量:一般定义在循环体外部所需要的开销要少的多,不是定义在循环体中的局部变量
- 尽量少做转型动作
- C++的四种转型手法:http://www.cnblogs.com/jerry19880126/archive/2012/08/14/2638192.html
- 如果可以尽量避免转型特别是在注重效率的代码中避免dynamic_casts,如果有个动作需要转型,试着写一个无需转型的动作代替
- 如果非得转型,试着将它藏在某个函数之后,客户可以随后调整该函数,而不是必须的将它放在代码中
- 宁可使用新型的转型方式也不要去使用旧的转型方式
- 避免返回handles指向对象内部成分
- 问题产生:
class point{ point(int x,int y); ... void setx(int newval); void sety(int newval);};struct rectdata{ point u; point l;};class rectangle{public: point & upperleft()const{ return pdata->u;} point & upperright()const{ return pdata->l;}private: std::str1::shared_ptr
pdata;};int main(){ point coord1(0,0); point coord2(100,100); const rectangle rec(coord1,coord2); rec.upperleft().sety(50);}这里我们能够通过最后的这个函数去修改private成员的值,那么私有成员变量就不是私有的了而是public的。最根本的原因是由于upperleft函数返回的是一个reference 指向rectangle对象的,所以私有变量就不是私有的了,失去了安全性和封装性 - reference,指针,迭代器都是handles中的一类,都是用来取得某个对象的一种工具,而返回代表一个内部对象的handles,他们很有可能修改成员变量和const成员函数,降低了类的封装性
- 解决方法:让函数加上const,这样就不能修改值,只能读取值了。
const point & upperleft()const{ return pdata->u;} const point & upperright()const{ return pdata->l;}
但是这又会产生一个新的问题,就是point&返回的对象可能发生它所指的东西不存在了。这个例子感觉比价深啊。详见书P126
- 解决方法:让函数加上const,这样就不能修改值,只能读取值了。
- 避免返回handles(包括reference,指针,迭代器)指向对象内部。可以增加封装性,帮助const成员函数的行为像个const,并发生虚掉号码牌的可能性降至最低。
- 问题产生:
- 为异常安全努力是值得的
- 问题的产生
class prettymenu{public: void changebackground(std::instream & imgsrc);private: Mutex mutex; Image *bgimage; int imagechanges;};void prettymenu::changebackground(std::istream&imgsrc){ lock(&mutex); delete bgimage; ++imagechanges; bgimage=new image(imgsrc);//这个异常会导致unlock无法调用,资源泄漏;new异常原互斥器没有解锁,然而imagechanges还是++了,但是并没有一个新的image产生,数据被破坏了 unlock(&mutex);}//这个例子没有遵守异常安全的两个条件//①不泄露任何的资源,②不允许数据的破坏 //unlock的资源泄漏问题可以很容易解决:利用lock class类就能解决,所以把最后一句删除即可。
- 解决方法:
- 异常安全提供三个保证:
- 强烈保证:如果异常被抛出了,程序中的所有东西仍然保持有效状态,任何数据结构或者对象会被破坏,抛出异常前后并没有发生变化。
- 基本保证:异常抛出,程序状态不改变。函数成功,就是完全成功,函数失败,程序回复到调用函数之前的状态。
- 承诺绝不抛出异常,总是能够完成要承诺完成的事情。
- 一般而言,只能够做到第一条和第二条。一个好的操作方法是:pimpl idiom方法:copy and swap,将所有隶属对象的数据从原对象放进另一个对象内,然后赋予原对象一个指针,指向那个所谓的实现对象(副本)。实现所有操作后在进行swap,这个提供的是强烈保证
struct PMImpl{ std::tr1::shared_ptr bgimage; int imagechanges;};class prettymenu{public: void changebackground(std::instream & imgsrc);private: Mutex mutex; std::tr1::shared_ptr
pimpl;//防止内存泄漏};void prettymenu::changebackground(std::istream&imgsrc){ lock m1(&mutex);//获得mutex的副本数据 str::tr1::shared_ptr pnew(new PMImpl(*pimpl));// pnew->bgimage.reset(new image(imgsrc));//修改副本 ++pnew->imagechanges; swap(pimpl,pnew);//置换数据,释放mutex} - 让函数具备异常安全性的方法步骤:①以对象管理资源:防止内存泄漏。②挑选三个异常安全保证中的一个实施与函数中,保证异常安全性,选择上应该选择第二个。
- 异常安全提供三个保证:
- 问题的产生
- 透彻了解inlining的里里外外(内联函数)
- inline函数的表示
- 隐式的:声明在类体内部,有些friend也可以是inline函数
class person{public: int age()const{ return theage;}private: int theage;};
- 显示的,在函数前面加上inline关键字
- 隐式的:声明在类体内部,有些friend也可以是inline函数
- inline函数通常都是置于头文件中的,在编译过程中进行inlining,必须知道函数本体长什么样子,才能将函数所有本体代码替换执行的inline函数。所以inlining一般都是编译期的行为。
- 由于inline是在编译期执行的,而虚函数时在运行的时候才将该函数具体化执行的。所以凡是虚函数一般都不是inline函数。所以,一个表面上看起来inline'的函数是否真的inline,取决于环境,主要屈居于编译器。如果无法inline,会发出一个警告信息。
- class类中的构造函数和析构函数一般是不能使用inline的,因为成本开销可能非常的大,但是表面上看不到它的开销,所以不适合使用。
- 将大多数inlining限制在小型的,频繁调用的函数上。不要只因为function出现在头文件,就将他们声明为inline。
- inline函数的表示
- 将文件间的编译依存关系降至最低
。