ldxcms

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

M1:指针和引用的区别
指针可以指向空值,但引用必须指向一个对象。
char *pc=0;
char& rc=*pc;//非常有害
不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高,因为在使用引用前不需要测试它的合法性。
void printDouble(const double& rd)
{
cout << rd; // 不需要测试rd,它
} // 肯定指向一个double值

void printDouble(const double *pd)
{
if (pd) { // 检查是否为NULL
cout << *pd;
}
}

指针与引用的另一个重要的不同是指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变。
当你重载某个操作符时,你应该使用引用。当你知道你必须指向一个对象并且不想改变其指向时,或者在重载操作符并为防止不必要的语义误解时,你不应该使用指针。而在除此之外的其他情况下,则应使用指针。

M2:尽量使用C++风格的类型转换

M3:不要对数组使用多态

class BST { ... };
class BalancedBST: public BST { ... };

void printBSTArray(ostream& s,const BST array[],int numElements)
{
for (int i = 0; i < numElements; ) {
s << array[i]; //假设BST类
} //重载了操作符<<
}

M4:避免无用的缺省构造函数

M5:谨慎定义类型转换函数
有两种函数允许编译器进行这些转换:单参数构造函数和隐式类型转换运算符。
单参数构造函数是指只用一个参数即可以调用的构造函数。该函数可以是只定义了一个参数,也可以是虽定义了多个参数但第一个参数以后的所有参数都有缺省值。
隐式类型转换运算符只是一个样子奇怪的成员函数:operator 关键字,其后跟一个类型符号。
让编译器进行隐式类型转换所造成的弊端要大于它所带来的好处。

M6:自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别
前缀形式返回一个引用,后缀形式返回一个const类型。
后缀需要返回一个临时对象,所以用前缀效率高。

M7:不要重载“&&”,“||”,或“,”

M8:理解各种不同含义的new和delete
new操作符(new operator)和 new操作(operator new)
new操作符是内置的,不能改变它的含义,它要完成两部分功能。第一部分是分配足够的内存,第二部分是调用构造函数初始化内存中的对象。
new操作符调用operator new,主要用来分配内存,可以重载operator new
placement new可以在指定地址上构造对象
delete操作符与operator delete关系类似

注意:如果用placement new在内存中建立对象,应该避免在该内存中用delete操作符。
因为delete操作符调用operator delete来释放内存,但是包含对象的内存最初不是被operator new分配的,placement new只是返回转递给它的指针。
谁知道这个指针来自何方?而应该显式调用对象的析构函数来解除构造函数的影响:

对于数组:new操作符调用 operator new[]。operator new[]也可以被重载。

M9:使用析构函数防止资源泄漏

M10:在构造函数中防止资源泄漏
用对应的auto_ptr对象替代指针成员变量,就可以防止构造函数在存在异常时发生资源泄漏,你也不用手工在析构函数中释放资源,并且你还能象以前使用非const指针一样使用const指针,给其赋值。

M11:禁止异常信息(exceptions)传递到析构函数外
禁止异常传递到析构函数外有两个原因,第一能够在异常转递的堆栈辗转开解(stack-unwinding)的过程中,防止terminate被调用。第二它能帮助确保析构函数总能完成我们希望它做的所有事情。
Session::~Session()
{
try {
logDestruction(this);
}
catch (...)
{ }
}

M12:理解“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的差异
不论通过传值捕获异常还是通过引用捕获(不能通过指针捕获这个异常,因为类型不匹配)都将进行拷贝操作,即使被抛出的对象不会被释放,也会进行拷贝操作。抛出异常运行速度比参数传递要慢。
当异常对象被拷贝时,拷贝操作是由对象的拷贝构造函数完成的。该拷贝构造函数是对象的静态类型(static type)所对应类的拷贝构造函数,而不是对象的动态类型(dynamic type)对应类的拷贝构造函数。

class Widget { ... };
class SpecialWidget: public Widget { ... };
void passAndThrowWidget()
{
SpecialWidget localSpecialWidget;
...
Widget& rw = localSpecialWidget; // rw 引用SpecialWidget
throw rw; //它抛出一个类型为Widget
// 的异常
}

在catch子句中进行异常匹配时可以进行两种类型转换。第一种是继承类与基类间的转换。
第二种是允许从一个类型化指针(typed pointer)转变成无类型指针(untyped pointer),所以带有const void* 指针的catch子句能捕获任何类型的指针类型异常。

M13:通过引用(reference)捕获异常

M14:审慎使用异常规格(exception specifications)
模板和异常规格不要混合使用。
如果在一个函数内调用其它没有异常规格的函数时应该去除这个函数的异常规格。
用其它不同的异常类型替换unexpected异常

M15:了解异常处理的系统开销
使用try块,代码的尺寸将增加5%-10%并且运行速度也同比例减慢。应该避免使用无用的try块。
编译器为异常规格生成的代码与它们为try块生成的代码一样多。

M16:牢记80-20准则(80-20 rule)
用profiler程序识别出令人讨厌的程序的20%部分。

M17:考虑使用lazy evaluation(懒惰计算法)

M18:分期摊还期望的计算

M19:理解临时对象的来源
当传送给函数的对象类型与参数类型不匹配时会产生临时对象。
仅当通过传值(by value)方式传递对象或传递常量引用(reference-to-const)参数时,才会发生这些类型转换。当传递一个非常量引用(reference-to-non-const)参数对象,就不会发生。
建立临时对象的第二种环境是函数返回对象时。

M20:协助完成返回值优化
特殊的优化――通过使用函数的return 位置(或者在函数被调用位置用一个对象来替代)来消除局部临时对象――是众所周知的和被普遍实现的。它甚至还有一个名字:返回值优化。

 

M21:通过重载避免隐式类型转换

M22:考虑用运算符的赋值形式(op=)取代其单独形式(op)
operator的赋值形式(operator+=)比单独形式(operator+)效率更高。做为一个库程序设计者,应该两者
都提供,做为一个应用程序的开发者,在优先考虑性能时你应该考虑考虑用operator赋值形式代替单独形式

,一种好方法是后者(指operator+ 译者注)根据前者(指operator+= 译者注)来实现

M23:考虑变更程序库

M24:理解虚拟函数、多继承、虚基类和RTTI所需的代价
virtual table和virtual table pointers。virtual table和virtual table pointers通常被分别地称
为vtbl和vptr。
一个vtbl通常是一个函数指针数组。(一些编译器使用链表来代替数组,但是基本方法是一样的)在程
序中的每个类只要声明了虚函数或继承了虚函数,它就有自己的vtbl,并且类中vtbl的项目是指向虚函数实
现体的指针。
每个声明了虚函数的对象都带有vptr,它是一个看不见的数据成员,指向对应类的virtual table。这个
看不见的数据成员也称为vptr,被编译器加在对象里。
虚函数不能是内联的。这是因为“内联”是指“在编译期间用被调用的函数体本身来代替函数调用的指令
,”但是虚函数的“虚”是指“直到运行时才能知道要调用的是哪一个函数。”

M25:将构造函数和非成员函数虚拟化
虚拟构造函数是指能够根据输入给它的数据的不同而建立不同类型的对象。
虚拟拷贝构造函数能返回一个指针,指向调用该函数的对象的新拷贝。
virtual TextBlock * clone() const // virtual copy
{ return new TextBlock(*this); } // constructor
类的虚拟拷贝构造函数只是调用它们真正的拷贝构造函数。

class NLComponent {
public:
// declaration of virtual copy constructor
virtual NLComponent * clone() const = 0;
...
};
被派生类重定义的虚拟函数不用必须与基类的虚拟函数具有一样的返回类型。如果函数的返回类型是一个
指向基类的指针(或一个引用),那么派生类的函数可以返回一个指向基类的派生类的指针(或引用)。

具有虚拟行为的非成员函数很简单。你编写一个虚拟函数来完成工作,然后再写一个非虚拟函数,它什么
也不做只是调用这个虚拟函数。为了避免这个句法花招引起函数调用开销,你当然可以内联这个非虚拟函数

M26:限制某个类所能产生的对象数量
你不能从带有private构造函数的类派生出新类,这个事实导致产生了一种阻止派生类的通用方法。

M27:要求或禁止在堆中产生对象
要求在堆中建立对象:最好让析构函数成为private,让构造函数成为public。可以引进一个专用的伪析
构函数,用来访问真正的析构函数。
通过限制访问一个类的析构函数或它的构造函数来阻止建立非堆对象,这种方法也禁止了继承和包容
(containment)。
通过把基类的析构函数声明为protected(同时它的构造函数还保持public)就可以解决继承的问题,需要
包含该类对象的类可以修改为包含
指向该类对象的指针。

禁止堆对象:把operator new声明为private就足够了,但是把operator new声明为private,而把
iperator delete声明为public,这样做有些怪异,所以除非有绝对需要的原因,否则不要把它们分开声明
,最好在类的一个部分里声明它们。因为operator new和operator delete是自动继承的,如果operator
new和operator delete没有在派生类中被声明为public(进行改写,overwrite),它们就会继承基类中
private的版本。

M28:灵巧(smart)指针

M29:引用计数

M30:代理类


M31:让函数根据一个以上的对象来决定怎么虚拟

M32:在未来时态下开发程序
:用C++语言自己来表达设计上的约束条件,而不是用注释或文档

M33:将非尾端类设计为抽象类

M34:如何在同一程序中混合使用C++和C
extern 'C' 意思是这个函数有C链接,但这个意思表达实在不怎么清晰。不管如何,它总意味着一件事:名
变换被禁止了。

M35:让自己习惯使用标准C++语言

posted on 2014-10-14 19:53  ldxcms  阅读(164)  评论(0编辑  收藏  举报