很多应用里面,会有相互联系而又细微不同的概念。
OOP(Object-oriented Programming)非常适合用在这样的场合。
15.1 Basic Concepts
----------------------------------------------------------------
OOP的核心思想是:
data abstraction
inheritance
dynamic binding
派生类必须在自己的类里,定义基类的所有virtual函数。
使用基类做参数的场合,我们也可以使用派生类,这里用到了dynamic binding的技术。
dynamic binding是直到运行的时候,才知道调用哪个类的virtual函数,因此它又称为:
run-time binding
15.2 Defining Base and Derived Classes
----------------------------------------------------------------
基类必须定义一个virtual的析构函数。
virtual关键字不可用在构造函数上,不可出现在函数的定义上(只能用在类中的函数声明)。
派生类继承的virtual函数,会被隐式的设置为virtual。
protected表示,这个成员可以被派生类访问,但不能被外部访问。
derived-to-base conversion:
我们可以将一个基类的引用或指针,绑定到派生类(的基类part)上,比如:
Bulk_quote bulk;
Quote *p = &bulk;
Quote &r = bulk;
派生类不能执行基类的成员初始化,因为:
Each class controls how its members are initialized.
如果派生类想实现基类的成员初始化,它需要在自己的构造函数中调用基类的构造函数。
比如下面的代码:
Bulk_quote(string isbn, double price) : Quote(isbn, price) {}
如果基类中有一个static成员,那么所有的派生类和基类都只会包含一个它。
声明派生类的时候,不能够加上后面的派生列表:
class Bulk_quote : public Quote; //error
class Bulk_quote; //OK
final关键字,用来告诉编译器这个类不能被继承。
15.3 Virtual Functions
----------------------------------------------------------------
当一个virtual函数被调用时,直到运行时编译器才知道应该调用哪一个版本。
这是通过引用或指针的dynamic type来判断的。
非virtual的函数在编译时就能指定。
当使用override关键字时,编译器会为我们检查此函数是不是override了基类中的函数。
virtual函数应该使用同样的基类default values参数,不管时基类还是派生类。
15.4 Abstract Base Classes
----------------------------------------------------------------
如果定义一个纯虚函数?
在函数的末尾添加“= 0”:
class Pure_quote : public Quote {
public:
Pure_quote() = default;
virtual double net_price(size_t n) const = 0;
};
这也意味着我们不能够定义Pure_quote的对象,而是只能继承它。
具备纯虚函数的类,称为抽象基类。
15.5 Access Control and Interitance
----------------------------------------------------------------
protected成员可以被friend或者派生类访问。
friend或者派生类只能通过派生的对象来访问,不能通过基类来访问,比如:
class Sneaky : public Base {
friend void clobber(Sneaky&); // can access Sneaky::prot_mem
friend void clobber(Base&); // can't access Base::prot_mem
int j; // j is private by default
};
如果派生的friend可以访问Base,那么只需要随意定义一个这样的派生类即可。
如果D类继承自B类:
* 只有当D类以public的方式继承B的时候,用户代码才可以使用derived-to-base转换。
* D的成员函数以及D的友元,能够使用这种转换,无论如何继承都可以。
* D派生类的成员函数以及它的友元,只有在public或者protected继承时,才能使用这种转换。
友元不可继承。
using可以用来改变访问的level。
15.6 Class Scope under Inheritance
----------------------------------------------------------------
派生类的scope nested in基类中。
派生类的name会覆盖它的基类中的name(不推荐使用派生类的name覆盖掉基类的name)。
scope操作符可以用来指定name的lookup。
派生类的函数会覆盖它的基类中的同名函数,即使它们的参数列表不一样,因为它们的scope不同。
15.7 Constructors and Copy Control
----------------------------------------------------------------
基类必须定义一个虚的析构函数。
当删除一个Quote指针时,可能实际删除的却是一个Bulk_quote指针,此时就要调用后者的析构函数。
当派生类进行copy操作时,它的基类会调用构造函数,除非我们显式的指定使用copy/move。
assig操作也是一样的。
如果基类没有定义assign操作或者copy/move,编译器会自动调用合适的copy或其他函数。
在构造函数或析构函数里面调用的virtual函数,一定是构造函数/析构函数本身的类所拥有的那个。
using关键字,可以用来使得派生类继承基类的构造函数,
比如:
class Bulk_quote : public Disc_quote {
public:
using Disc_quote::Disc_quote; // inherit Disc_quote's constructors
double net_price(std::size_t) const;
};
15.8 Containers and Inheritance
----------------------------------------------------------------
一般来说,和继承相关的类,不要存放在容器中,
因此我们很难确认,是存基类还是存派生类;使用指针(smart指针)可以解决这个问题。
用户593939 2016-4-29 22:58