关于函数体 try 的几个要点:
- 构造函数 try 只能用来传递从基类或成员子对象的构造函数抛出的对象(或者做一些响应这些错误的其它操作),不能用作其它用途(比如释放资源)。
- 析构函数 try 没有什么实际用途,因为析构函数不应该抛出异常。
- 所有其它的函数 try 没有什么实际用途。一个一般的函数 try 不能抓取到函数体内的 try 所不能抓取到的异常。
- 记住在构造函数体内而不是在初始化列表中分配未被管理的资源(比如使用 new)。即,要么使用“获取资源就是初始化”,从而避免未被管理的资源,要么在构造函数体内获取资源。
- 总是在构造函数或析构函数体内释放未被管理的资源,而不是在它们的函数 try 里面。
- 如果一个构造函数有异常规格,那么它的异常规格必须包含该类的基类和成员子对象的所有可能抛出的异常。
- 使用 Pimpl 思想来保持类内部的“可选的部分”。如果一个成员对象的构造函数可能抛出异常而你不一定需要那个对象,就可以在类中仅仅保持一个指向该对象的指针。通过该指针是否为空来判断是否持有改对象。
- 最后重复一下:使用“资源分配就是初始化”而不是手动管理资源。
文章来源:
http://my.donews.com/robinchow/2007/01/29/cagxxmzwktgtrdirlmuspfjuarcxkcumdbjb/
标准 uncaught_exception() 函数提供了对是否还有活动异常的判断。非常值得注意的是,它不表明是否可以安全地抛出异常。
指导原则:
- 千万不要从析构函数内抛出异常。原因是如果当存在另一个活动异常,而析构函数又抛出一个异常时(堆栈展开时),程序将被终止。写析构函数时加上空的异常规格,因为异常规格将带来额外的代价,因此可以将它写成注释的形式。
- 如果析构函数调用一个可能抛出异常的函数,一定要将它包含在一个 try/catch 块内以防止异常传出。
最后一点,uncaught_exception() 没有安全的用途,因此建议不要使用。
文章来源:
http://my.donews.com/robinchow/2007/01/29/jxfinmwnxhfofnvifzmrkqjgteyknbfxzlfn/
关于参数表达式求值:
- 在函数调用之前,函数的所有参数都必须完成求值。这包括了参数表达式求值的所有副作用。
- 一旦函数开始执行,则调用函数的所有表达式要等到被调函数执行完成之后才开始或继续求值。函数的执行不会相互间接。
- 函数的参数表达式求值的顺序不定,可能互相间接。
f(new T1, new T2) 可能导致一个经典的内存泄漏问题。加入 new T1 先执行,new T2 后执行,则一旦 new T2 的过程抛出异常,T1 的内存将被泄漏。
文章来源:
http://my.donews.com/robinchow/2007/01/29/vcdrqtzdfdhbnkjyqgipqoiapasnrtvmwydm/
关于临时对象:临时对象在包含它的创建点的全部表达式的最后一步被销毁。甚至在全部表达式抛出异常时也是如此。
编码标准:不要在同一表达式中编写分配资源而且可能抛出异常的代码。即使分配的资源会立即被管理(例如传递给 auto_ptr 的构造函数)。
更简单的方法是:对每个单独的资源分配,都放在单独的代码片断里,而不是将几个资源分配放在同一个表达式中。然后再将分配的资源传递给管理对象(auto_ptr)进行管理。
文章来源:
http://my.donews.com/robinchow/2007/01/30/pvtqocosqslbdxledtwmymsmgcfifqavmula/
设计方法:
低耦合有利于程序的正确性(包括异常安全),而高耦合则减降低了程序最大可能的正确性。
继承通常被滥用,甚至一些经验丰富的开发人员也是如此。尽量降低耦合。如果类的关系能够用多种方式表达,那么选用关系最弱的方式。特别是,只有在代理不够用的时候才使用继承。
文章来源:
http://my.donews.com/robinchow/2007/02/03/veyjfchfekvqisvephpmjlwafvknmahifgrl/
关于多继承(MI):
避免从超过一个的非协议类进行多继承。协议类是指抽象基类,仅由纯虚函数组成,而没有数据。
实际使用多继承时不外乎一下三种情况:
- 将几个模块或者库结合起来。
- 协议类。这是多继承最好、最安全的用法。
- 易用性(多态)。
另外,记住有时候不是仅仅从两个不同的基类继承,而是以不同的理由各自进行继承。例如,可以对一个基类进行私有继承以访问基类的保护成员,同时对另一个基类进行公有继承以实现多态。
文章来源:
http://my.donews.com/robinchow/2007/02/03/cbkweueolxssxwokmhjkcwizyhjjhdfkepgj/
纯虚函数是具体派生类必须重载的虚函数。如果一个类有一个没有重载的纯虚函数,那么它是一个抽象类,不能创建该类型的对象。
定义纯虚函数体的原因有以下死点:
- 纯虚析构函数。通常情况下,析构函数要么是虚函数并且为公有,要么是非虚函数并且为 protected。如果没有提供函数定义,则不能实例化该基类的派生类。
- 强迫程序员认识到正在使用缺省行为。对于普通虚函数,如果派生类没有重载则默认使用基类的缺省行为。如果不想隐含地使用基类的行为,可以使基类的虚函数为纯虚函数,则派生类必须显式地使用基类的行为。
- 提供部分行为。
- 帮助编译器诊断错误。如果不小心调用了一个纯虚函数(通常是对构造函数或析构函数间接调用),编译器可能查不到问题所在,这是可以在纯虚构函数里面写一些使程序崩溃的代码,从而方便进行查错。
文章来源:
http://my.donews.com/robinchow/2007/02/05/rhawrggrcrbpbeslbvcspydjubkcgkwxswuj/
局部类型或匿名类不能用作模板参数。
C++ 不支持函数嵌套,但是我们可以通过函数对象来间接实现它。
#
数字符(或称‘字符串化’操作符)把扩展后的宏参数转化成字符串常量。 它仅与带参数的宏一起使用。
如果它在宏定义中定义在形参前面,那么由宏调用传递进来的实参是被引号括起来的,并且被当做一个字符串语意来对待。
这个字符串会替代每一个在宏定义中出现的‘字符串化’的操作符和形参。
符号传递操作符
双数字符(或称“符号传递”操作符),有时也被称为合并操作符,用在类似对象以及类似函数的宏中。 它允许分开的若干符号被合并成一个符号,因此它(##)不能作为宏定义中第一个或者最后一个出现的符号。
除了以下情况,尽量避免使用预处理宏:
- #include 守卫
- 增强可移植性或方便调试的条件编译(在 cpp 文件中,而不是在 .h 文件中)。
- 使用 #pragmas 来禁止一些无意义的警告。#pragmas 通常应该放在 “移植性的条件编译” 守卫中以防止编译器不能识别该指令而发出警告。
文章来源:
http://my.donews.com/robinchow/2007/02/11/wkibsjwatiheajluxlmvybkwnutbvifrtotm/
使用宏时须注意:
- 参数两边要加括号
- 整个表达式两边要加括号
- 特别注意表达式多次求值的情况,如 max(++i, j)
- 宏会搅乱名字空间
- 宏不能递归
- 宏没有地址
- 宏不利于调试
On a compliant compiler, it is not possible for a macro to create any of the following:
a trigraph (trigraphs are replaced in phase 1);
a universal character name (\uXXXX, replaced in phase 1);
an end-of-line line-splicing backslash (replaced in phase 2);
a comment (replaced in phase 3);
another macro or preprocessing directive (expanded and executed in phase 4); or
changes to a character literal (for example, 'x') or string literal (for example, "hello, world") via macro names inside the strings.
文章来源:
http://my.donews.com/robinchow/2007/02/11/bavcdirqeacybkdedpxorhuddpwyehtghojl/
将字符转换为字节:
byte[] byteTime = Encoding.ASCII.GetBytes(DateTime.Now.ToString());
将字节转换为字符:
string sTime = Encoding.ASCII.GetString(bytes,0,bytesRead);
文章来源:
http://my.donews.com/robinchow/2007/03/25/c-%e4%b8%ad%e5%ad%97%e7%ac%a6%e5%92%8c%e5%ad%97%e8%8a%82%e7%9b%b8%e4%ba%92%e8%bd%ac%e6%8d%a2/