1.尽量以const,enum,inline替换define。
2.旧式的编译器可能不允许static在其申明式上获得初值。此外所谓的‘in-class’初值设定也许只针对整数常量进行,要是你的编译器不支持上述语法,可以将初值放在定义式。要是数组无法初始化时可以考虑使用类中的枚举值。
3.如果成员变量是const或者reference一定要用初值发初始化而不要使用赋值法。
4.构造函数最好使用成员初始化列表而不要在构造函数体内使用赋值操作,成员初始化列表列出的值应该与他们在class中的申明次序相同。
5.位避免跨编译单元之初始化次序问题,请以local static 对象代替non-local static对象。
6.任何class只有含有虚函数都几乎确定应该有个虚析构函数。
7.string,还有所有STL容器如vector、list、set等都不带虚析构函数。
8.析构函数绝对不要吐出异常,如果一个被析构函数调用的的函数可能抛出异常,析构函数应该能够捕获异常,并且吞下异常或者结束程序。
9.如果客户对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数而非析构函数中执行该操作。
10.base class函数构造期间virtual函数绝不会下降到derived class阶层,对象作为就像隶属于base类型一样,非正式的和说法就是virtual函数不是virtual函数。而base class函数 早于derived class 的构造函数,base构造时derived的变量还未初始化。相同道理也适用于析构函数,一旦derived的析构函数执行,对象内的derived成员变量呈现未定义值,进入base的析构函数后就成为一个base class对象,而C++的任何部分包括virtual函数dynamic_casts也会那么看待它。一个优秀的做法就是把共同的实化代码放入一个init函数内(非virtual),这么做还是可能存在问题,最好不要在构造函数和析构函数中调用virtual函数。
11.令赋值操作符返回一个reference to *this .
在operator中处理自我赋值:低级方法--进行自我复制检查。高级方法--保存副本。
12.Copying函数应该确保赋值对象内的所有成员变量及base class的成分。不要尝试以一个Copying构造函数初始化另一个Copying构造函数,应将共同技能放在第三个函数中。
13.以独立语句将newed对象存储与智能指针内。如果不这样做,一旦异常抛出,有可能导致难以察觉的资源泄漏。
14.绝不要返回pointer或者reference指向一个local stack对象,或者返回前指向一个heap-allocate对象(可能没有人delete它)
15.类中的纯虚函数可以定义自己的实现,但这可以通过派生类对象或者指向派生类的指针显示调用
class A
{
public:
virtual void fun1() = 0;
};
void A::fun1()
{
cout << "void A::fun1()" << endl;
}
class B:public A
{
public:
virtual void fun1()
{
cout << "void B::fun1()" << endl;
A::fun1();//这样也可以
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A *q = new A;
q->fun1(); //error
A *p = new B;
p->fun1();
p->A::fun1();
system("pause");
return 0;
}
16.derived class 内的名称会掩盖base class内的名称。通过基类指针指向派生类可以防止名称掩盖而无法正确调用,实际上是利用的多态。
class A
{
public:
virtual void fun1() = 0;
virtual void fun1(int n)
{
cout << "void A::fun1(int n)" << endl;
}
virtual void fun2()
{
cout << "void A::fun2()" << endl;
}
void fun3()
{
cout << "void A::fun3()" << endl;
}
void fun3(int n)
{
cout << "void A::fun3(int n)" << endl;
}
};
class B:public A
{
public:
virtual void fun1()
{
cout << "void B::fun1()" << endl;
}
void fun3()
{
cout << "void B::fun3()" << endl;
}
void fun4()
{
cout << "void B::fun4()" << endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
// B b;
// int n = 250;
// b.fun1();
// b.fun1(n); //error,作用域被覆盖必须显示调用b.A::fun1(n)
// b.fun2();
// b.fun3();
// b.fun3(n); //error,作用域被覆盖必须显示调用b.A::fun3(n)
// B *p;
// int n = 250;
// p->fun1();
// p->fun1(n); //error,作用域被覆盖必须显示调用p->A::fun1(n)
// p->fun2();
// p->fun3();
// p->fun3(n); //error,作用域被覆盖必须显示调用p->A::fun3(n)
//通过基类指针指向派生类可以防止名称掩盖而无法正确调用,实际上是利用的多态
A *a = new B;
int n = 250;
a->fun1();
a->fun1(n);
a->fun2();
a->fun3();
a->fun3(n);
B *p = new B;
p->fun1();
p->fun1(5); //error,派生类重写基类的同名称函数后会掩盖base class内的名称,无法调用基类的fun1(int n),如果没有重写的话可以调用基类的对象函数
p->fun2();
p->fun3();
p->fun3(6) ;//error,派生类重写基类的同名称函数后会掩盖base class内的名称,无法调用基类的fun1(int n),如果没有重写的话可以调用基类的对象函数
p->fun4();
system("pause");
return 0;
}
17. non-virtual函数A::fun3和B::fun3都是都是静态绑定的,意识就是q被申明为pointer-to-A,通过p调用的non-virtual函数永远是A所定义的版本,即使q指向的
一个类型为A的派生class对象。另一方面,virtual函数确实动态绑定,所以他们不会受此问题之苦,如果fun3是个virtual函数,不论p还是q调用fun3,都会导致调用
B::fun3,因为他们真正指的都是一个类型为B的对象。
接上面类A、B
B *p = new B;
p->fun3(); //调用的是B的fun3
A *q = new B;
q->fun3(); //调用的是A的fun3
18. 请绝对不要重新定义一个继承而来的non-virtual函数,如果需要这么做,一开始就应该考虑设计为一个virtual函数
19. 绝不要重新定义继承而来的缺省参数值,virtual函数是动态绑定,调用一个virtual函数时,究竟调用哪一份函数实现代码,取决于调用的那个对象的鼎泰类型,
而缺省参数值确是静态绑定,静态绑定下函数并不从其base继承缺省函数值