一、对象使用前确定初始化 — Item 4
类对象在使用前必须保证其初始化,注意和赋值的区别。虽然赋值操作有时可以实现相同的操作,但是某些时候可能会出问题。因此类的构造函数最好使用成员初值列来实现对象数据的初始化,而不是在函数中进行赋值。例:
class Test{
public:
Test(int a, int b);
private:
int numx;
int numy;
int numz;
};
//这里这个构造函数是采用赋值的方式给对象成员赋值
Test::Test(int a, int b){
numx = a;
numy = b;
numz = 0;
}
//这里这个构造函数是采用成员初值列的方式实现对象成员初始化
Test::Test(int a, int b):numx(a),numy(b),numz(0){}
C++规定,对象的成员变量初始化操作发生在进入构造函数本体之前,因此,上面的第一个Test不是初始化,而是赋值,成员变量numx,numy,numz的初始化时间发生更早,是通过默认的构造函数进行初始化的。第二个Test与前一个结果相同,但是效率更高。这里注意成员初值列的顺序要与声明顺序相同,在C++中类成员变量总是以其声明的次序初始化。
二、C++默认编写并调用的函数 — Item 5
在C++类中,如果没有声明,则编译器会自动声明下面几个函数:default构造函数,copy构造函数,copy assignment构造函数,析构函数。copy构造函数就是利用对象来新建新的对象,copy assignment就是通过=来新建对象。
若是有时不想编译器默认声明这几个函数,可以将相应的成员函数声明为private并且不予实现,这样在调用这样的函数时编译器就会提示拒绝。(I-6)
class A //类,当不进行定义时,会有下面的函数自动定义
A(){}
A(const A&) {}
A& operator=(const A&){}
~A(){}
//分别表示下面的用法
A s1= A(); A(s2); A s3 = s2;
//只有定义了相应类型函数才能使编译器不提供默认版本
三、以对象管理资源 — Item 13
在C++中管理资源是十分重要的,当一个对象创建之后并使用之后需要删除它,但是在函数中有时可能无法做到绝对的会执行删除命令,这里可以用到auto_ptr指针来进行管理。C++的auto_ptr所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理。用法如下:
int *p=new int(0);
auto_ptr<int> ptr1(p);
上面的指针p更多的是一个对象指针,auto_ptr类指针对象ptr1则用来管理p,当p用完时可以自动销毁。
auto_ptr注意事项:
1)auto_ptr不能共享所有权,即不要让两个auto_ptr指向同一个对象。
2)auto_ptr不能指向数组,因为auto_ptr在析构的时候只是调用delete,而数组应该要调用delete[]。
3)auto_ptr只是一种简单的智能指针,如有特殊需求,需要使用其他智能指针,比如share_ptr。
4)auto_ptr不能作为容器对象,STL容器中的元素经常要支持拷贝,赋值等操作,在这过程中auto_ptr会传递所有权,那么source与sink元素之间就不等价了。
参考:http://www.cnblogs.com/qytan36/archive/2010/06/28/1766555.html
源码参考链接。
四、尽量使用const引用传值 — Item 20
在函数调用时,经常会有参数的存在,若是一般的简单参数还好,可以直接将参数的值进行传递,但是有时会有一些复合类型的参数,例如某个类对象,或某个大的数组,若是直接传递值的化,函数调用过程中,会进行这些值的复制,对于类对象的参数还会创建对应参数的对象副本,这些新建的副本在函数调用完之后又要进行销毁,这样一个过程是比较浪费资源的。因此,在大多数时候,可以尽量采用引用的方式来进行参数传递(类似于指针传递),利用by-reference-const进行传值比较高效,且可以避免切割问题。当然这种方式不适合内置类型及STL。
五、封装性 — Item 22
一般类成员变量都应声明为private,这样拥有最好的封装性,只有通过定义的成员函数才可以对这些变量进行适当的访问。protect的封装性并不比public强,即这两者的封装性都很差。
六、继承与面向对象设计
对于public继承来说,基类的所有特性都适用于派生类,因为派生类对象也是一个基类对象。(I-32)
派生类中的名称会掩盖基类中的名称(主要为函数),只与名称有关,与相应的函数参数无关,函数一旦在派生类中作用域中调用则先在此作用域查找函数,若是没有,则再扩大范围,在基类作用域中查找(C++的名称遮掩规则-作用域)。(I-33)
在类继承中,函数的继承相当重要,在基类中函数的类型有几种:纯虚函数,一般虚函数,普通函数。纯虚函数和虚函数都是在函数声明前面加上virtual,纯虚函数的话声明时直接让函数等于0。
纯虚函数在基类中没有定义,在派生类中需要重新声明,再根据需要定义函数体。在这里派生类只是继承一个接口,并无法获得具体的实现继承,具体的实现必须由派生类自己定义。
一般虚函数与纯虚函数区别就在于它提供了一个默认的实现继承,派生类继承这个接口,但是可以根据需要来决定是否重新定义函数体,如果不想重新定义,可以使用默认继承,因为基类中虚函数提供了一个默认的实现,这在纯虚函数中是没有的。(I-34)
一般函数就是非虚函数,它也提供一个接口,但是也提供实现,且是强制性的实现继承,不能进行改变。虽然在派生类中可以对基类的一般函数进行修改覆盖,但是并不推荐这样做,因为对于一般函数它们是静态绑定的,即调用哪个函数由对象的指针类型来决定。(I-36)
策略模式Strategy(对象行为型)。 (I-35)
静态类型,对象在声明时所采用的类型,动态类型,对象当前所指的对象类型。一般出现于类继承中,可以用基类来定义一个对象,则该对象的静态类型为该基类,再将该对象指向一个派生类的对象,则此时,该对象的动态类型变为了派生类类型。在函数继承中不要重新定义一个继承而来的缺省参数值,缺省参数是静态绑定的,而继承的virtual函数唯一应该覆盖的东西是动态绑定。(I-37)