1. 在构造函数中完成工作
(1)仅完成成员变量的初始化,其他复杂的初始化由 Init() 函数完成。
(2)构造函数中不允许使用异常处理。
(3)如果构造函数是虚函数,则其调用不会传至子类,可能引起错误,访问未初始化的全局变量等问题。
2. 默认构造函数
(1)类定义了成员变量,而没有其他构造函数,则应该定义默认构造函数,在函数内对成员变量初始化。
(2)创建一个类对象,而不传入参数时,会调用默认构造函数。
(3)继承自其他类,且未增加成员变量的子类,不需要再定义默认构造函数。
3. 显式构造函数
(1)explicit 用于仅有一个参数的构造函数,可避免产生类型转换。
(2)只接受一个参数的构造函数可用于类型转换。如 Foo::Foo(string name),向以 Foo 类型为参数的函数传递 string 参数时,将调用这个构造函数完成 string 到 Foo 的转换。
(3)只有一个参数的构造函数,但不使用 explicit 的例外情况
a. 复制构造函数;
b. 打算作为透明封装的类;
(4)示例
class Foo{public: // foo.Func(std::string("hello")); 编译错误,explicit 避免了类型转换,创建临时对象 explicit Foo(const std::string &name) { } void Func(const Foo &foo) { }};
4. 复制构造函数
(1)限制编译器自动生成复制构造函数和赋值构造函数的方法。
方法一:
a. 宏定义
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&); \ void operator=(const TypeName&)
b. 作用
声明私有的复制构造函数和赋值构造函数,但不定义实现,可限制编译器自动生成,及外部的调用;
未定义实现,可在成员函数、友元函数调用时,产生连接错误;
c. 示例
class Foo{public: Foo(void); ~Foo(void); private: DISALLOW_COPY_AND_ASSIGN(Foo);};
方法二:
a. 示例
class CUncopyassign{private: CUncopyassign(const CUncopyassign&); CUncopyassign& operate= (const CUncopyassign&);};class Foo : public CUncopyassign{// 不再声明复制构造函数、赋值构造函数};
b. 作用
成员函数或友元函数拷贝 Foo 对象时,编译器生成复制构造函数、赋值构造函数,并调用基类的对应函数,这些函数是私有的,产生编译错误;
(2)利:复制构造函数方便对象的复制,将构造和复制进行了结合。
(3)弊:对象的显示复制常会导致缺陷和引起性能问题,降低代码的可读性。
(4)如果类需要复制性,提供复制方法,如 CopyForm() 或 Clone(),而不使用不能被显示调用的复制构造函数。
(5)复制方法不能满足要求(如保存在STL容器)时,可提供复制构造函数、赋值构造函数。
5. 结构体与类
(1)对象只是用来保存数据时,使用结构体,其他情况则使用类。
6. 继承
(1)组合通常比继承更合适。
(2)使用继承时,一般为公有继承。
(3)继承的两种方式:实现继承,实质性代码都被子类继承;接口继承,子类只继承接口名称;
(4)利:实现继承重用基类代码来减小程序规模。接口继承通过编程使一个类对外暴露特定的API。
(5)弊:实现继承,子类的实现代码需在基类和自身展开,理解这些实现变得困难,子类不能覆盖非虚方法。
(6)定义了虚函数的类,析构函数都应该被虚化。
(7)子类使用基类方法时,用 protected 加以限制。
(8)重写继承的虚函数时,在子类中显示声明其为 virtual。
7. 多重继承
(1)只有但仅一个基类被实现继承,而其他类都是纯接口,且以 interface 作为后缀声明时才允许多重继承。
8. 接口
(1)满足一下条件的类被称为纯接口
a. 只有公共的纯虚函数和静态方法;
b. 只有静态数据成员;
c. 没有构造函数定义,或声明为 protected 的默认构造函数;
d. 继承满足以上条件且以 interface 后缀结束的基类的子类;
9. 运算符重载
(1)利:像内建类型一样操作,代码更直观。
(2)弊:
a. 误以为大开销的操作,是小开销的内建操作;
b. 找到重载运算符的调用点比较困难;
c. 一些运算符也适用于指针,但容易造成程序缺陷;
d. 可能造成歧义;
(3)一般情况下,不重载运算符,尤其是赋值运算符。如果一个类需要被前置声明,应避免单目运算符 & 的重载。
10. 访问控制
(1)数据成员应该被定义成私有,静态数据成员除外。
11. 声明次序
(1)方位次序
a. public;
b. protected;
c. private;
(2)成员次序
a. 类型定义(typedef)和枚举(enum);
b. static、const 数据成员;
c. 构造函数;
d. 析构函数;
e. 类方法;
f. 数据成员;
12. 定义简短函数
(1)函数应该简短且功能单一。
(2)函数超过40行时,应注意考虑在不改变程序结构的情况下将其拆分。