考虑如下情形
BaseClass *ptr = createChildClassInstance();
// use ptr
delete prt;
如果不用 virtual 解构函数,delete 时候会出错。
2. 使用模板类必须指定模板实参(argument)
使用模板函数可以不指定模板实参,但是当编译器没办法推断出类型实参时,则必须指定
template<typename T> int compare(const T&a, const T&b) { if(a<b) return -1; if(b<a) return 1; return 0; } int main() { cout << compare(1,2) << endl; cout << compare<double>(1,2.0) << endl;//必须指定 cout << compare<string>("abc","abcd") << endl;//必须指定 return 0; }
3. 继承的选择
Public 继承说明``is-a''关系,基类的任一行为派生类都能够执行。想想鸟类和企鹅在飞行这个动作上的趣事,还有矩形可以增加长度而不改变宽度,正方形是矩形却不能这样做。
如果一个类不准备作为基类,那么全部函数都是 non-virtual。对于一个基类,其成员函数有三种选择,我们根据需要选择一种。意义分别如下(以 Person 类为例):
- pure virtual:只继承接口,继承类必须实现。强调特异性。比如“打招呼”这个成员函数,由于语言各异,可以设为 pure virtual.
- impure virtual:继承接口和一份缺省实现。特异性和不变性的平衡。比如“表示同意”这个函数,缺省为点头,这是大多数人的习惯。当然也有不同文化的人群用摇头表示同意。
- non-virtual:继承接口和一份强制实现。强调不变性。比如求身高这个函数。不同的人的求法都是一样的。
绝不重新定义继承而来的 non-virtual 函数。绝不重新定义继承而来virtual 函数的缺省参数值。
Virtual 接口的另一选择是 non-virtual interface (NVI).
Private 继承意思是is-implemented-in-terms-of,不继承接口,只继承实现,并利用有用的实现。
基类的所有 public 和 protected 成员都变成派生类的 private 成员,这是表明派生类并不表现为基类,我们只是利用基类的一些实现。
不要用 protected 继承。
4. 多态和虚函数
#include类 base 和 derived 都包含虚函数,称抽象基类。derived 中函数可以加上修饰符 virtual,含义是一样的。 声明 virtual 后,定义函数体时可不加 virtual,不算重载。但是定义函数体时如果去掉 const,算重载。#include "early.h" using namespace std; int base::f() const { return 7; } int derived::f() const { return 2;} int main() { derived d; base* b1 = &d; base& b2 = d; base b3; // Late binding for both: cout << "b1->f() = " << b1->f() << endl; cout << "b2.f() = " << b2.f() << endl; // Early binding (probably): cout << "b3.f() = " << b3.f() << endl; }
编译器为包含虚函数的类生成 VTABLE,为其对象生成 VPTR,因此base 和 derived 都有VTABLE 和 VPTR。
纯虚函数(function\_name() = 0)告诉编译器在 VTABLE中为函数保留一个间隔,但不放地址,其 VTABLE 是不完整的,不能生成这个类的对象。包含纯虚函数的类成纯抽象类。注意为纯虚函数定义函数体是可以的,派生类可以通过
base::function\_name 来访问。
抽象基类作用是为子类提供一个公共借口。
5. 名字掩盖Name hiding
几个名词
signature 函数的参数列表
overload 指同一个类(同一层次)中多个函数的函数名一样,signature不一样
redefine 指子类中对父类的函数重定义,signature和return type都要一样(const 好像不影响),override也是一种redefine
override redefine并且父类的函数为虚函数
若父类有overload的函数,子类对其中一个进行redefine,所有其他的版本(除redefine的那个)都会被hidding
子类对其中一个进行修改(signature或return type),则所有版本都被hidding
6. 枚举常量不会占用对象的存储空间,它们在编译时被全部求值
7. 不要重新定义继承而来的数据成员
0 comments:
Post a Comment