第15章 面向对象编程
1 面向对象编程基于三个基本概念:数据抽象、继承和动态绑定。
2 继承层次共享共用的东西,仅仅特化本质上不同的东西。
3 定义为virtual的函数是基类期待派生类重新定义的,基类希望派生类继承的函数不能定义为虚函数。
4 在C++中,通过基类的引用(或指针)调用虚函数时,发生动态绑定。引用(或指针)既可以指向基类对象也可以指向派生类对象,这一事实是动态绑定的关键。用引用(或指针)调用的虚函数在运行时确定,被调用的函数时引用(或指针)所指对象的实际类型所定义的。
5 除了构造函数和static成员函数,其他函数都可以定义为虚函数。保留字virtual只在类内部的成员函数声明时出现,不能用在类定义体外部出现的函数定义上。
6 Protected的另一重要性质:
派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限。
7 派生类中虚函数的声明必须与基类中定义方式完全匹配,但有一个例外:派生类中的虚函数可以返回基类函数所返回类型的裴胜磊的引用(或指针)。
Item_base类可以定义返回Item_base*的虚函数
Bulk_item类中定义的实例可以定义为返回Item_base*或Bulk_item*.
8 要触发动态绑定,必须满足两个条件:
a) 第一,只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不进行动态绑定。
b) 第二,必须通过积累类型的引用或指针进行函数调用。
9 引用和指针的静态类型与所绑定的对象的动态类型可以不同,这是C++用以支持多态性的基石。
10 派生类虚函数调用基类版本时,必须显式使用作用域操作符。如果派生类函数忽略了这样做,则函数调用会在运行时确定而且将是一个自身调用,从而导致无穷递归。
11 虚函数可以有默认实参,如果一个调用省略了具有默认值的实参,则所用的值由调用该函数的类型定义,与对象的动态类型无关。在同一虚函数的基类版本和派生类版本中使用不同默认实参几乎一定会引起麻烦。
12 派生类可以进一步限制但不能放松对所继承的成员的访问。
13 通过using声明访问基类中的名字。
14 友元关系不能继承。
15 function(object & a)和 function(object a)object派生类对象做实参调用时的区别:
a) 一个是将派生类对象转换成基类类型引用。
b) 一个是用派生类对象对基类对象进行初始化或赋值。
16 编译器确定转换是否合法,只看指针或引用的静态类型。
Bulk_item bulk;
Item_base *itemP = &bulk;
Bulk_base *bulkP = itemp; //error:can’tconvert base to drived
17 派生类的构造函数先初始化基类部分,再初始化自身特化部分。
派生类构造函数的初始化列表只能初始化派生类的成员,不能直接初始化继承成员。相反,派生类构造函数通过将基类包含在构造函数初始化列表中来间接初始化继承成员。
Bulk_item(const string&book,double salses_price,size_t qty = 0,double disc_rate = 0.0):
Item_base(book,salse_price),min_qty(qty),discount(disc_rate){ }
18 只包含类类型或内置类型数据成员、不含指针的类一般可以使用合成操作,复制、赋值或撤销这样的成员不需要特殊控制。具有指针成员的类一般需要定义自己的复制控制来管理这些成员。
19 如果派生类显式定义了自己的复制构造函数或赋值操作符,则该定义将完全覆盖默认定义。被继承类的复制构造函数和赋值操作符负责对基类成分以及类自己的成员进行复制和赋值。
20 指针的静态类型与被删除对象的动态类型不同,所以需要虚析构函数。
21 构造函数和赋值操作符不是虚函数:
a) 构造函数时在对象完全构造之前运行的,在构造函数运行的时候,对象的动态类型还不完整。
b) 将类的赋值操作符设为虚函数很可能会令人混淆,而且不会有什么用处。
22 如果在构造函数或析构函数中调用虚函数,则运行的是为构造函数或析构函数自身类型定义的版本。
解释:构造派生类对象时首先运行基类构造函数初始化基类部分,此时对象还不是一个派生类对象。同理析构函数也是。
23 如果派生类想通过自身类型使用所有的重载版本,则派生类必须要么重定义所有重载版本,要么一个也不重定义。有时候类需要仅仅重定义一个重载集中某些版本的行为,并且想要继承其他版本的含义。
Using声明
24 理解C++中继承层次的关键在于理解如何确定函数调用:
a) 确定函数调用对象的静态类型
b) 查找名字,溯流而上。
c) 常规类型检查
d) 是否动态绑定
25 含有纯虚函数的类为抽象类,除了作为抽象类的派生类的对象的组成部分,不能创建抽象类型的对象。
26 一个保存基类对象的容器,当加入派生类对象时,将发生截断。解决方法:
a) 保存对象指针,但用户需面对管理对象和指针的问题。
b) 句柄类(用户获得动态行为但无须操心指针的管理,智能指针就是句柄类的例子)。