关于基类和派生类之间的关系

     作为面向对象的程序设计来说,继承是非常重要的一个特点,面向对象程序设计(Object-Oriented Programming, OOP)的核心思想是数据抽象、继承和动态绑定。其中使用数据抽象,我们可以将类的接口与实现分离;使用继承,可以定义相似的类型并对其相似关系建模;使用动态绑定,可以在一定程度上忽略相似类型的区别,而以统一的方式使用它们的对象。下面什么主要介绍的是继承中基类和派生类之间的关系。

注:动态绑定只有当我们通过指针或者引用调用虚函数时才会发生。

     在基类中有两种类型的成员是我们需要注意的,一种是基类希望派生类继承的;一种是基类希望派生类覆盖的。如:基类Quote和其派生类Bulk_quote.

class Quote
{
     public:
              std::string isbn() const;
              virtual double net_price(std::size_t n) const;
}

  这里成员函数isbn()是基类希望派生类继承的,派生类中不需要重新定义,而net_price是希望派生类覆盖的,派生类中有不一样的实现方式。这里基类Quote将希望派生覆盖的成员函数定义成虚函数,即在前面加上:virtual关键字。

      那么,那些成员函数需要定义成虚函数了,即基类和派生类中都有各自的实现方式的时候,就需要在基类中将该成员函数定义成虚函数,希望派生类将其覆盖。动态绑定即是在我们使用基类的引用(或者指针)调用一个虚函数时发生的。这样程序就可以根据参数的不同来判断是调用基类的成员函数,还是调用派生类的成员函数。当然派生类也不是一定要重新定义基类中的虚函数,如果派生类没有覆盖基类中的虚函数,那么该虚函数就和其他普通函数一样,派生类就直接继承它在基类中的版本。

派生类和基类之间的类型转换

     上面例子中基类Quote里面有成员:bookNo和price,派生类Bulk_quote中有:min_qty和discount.当派生类继承基类的时候,派生类中就有两部分(四个)成员:一部分是继承Quote的bookNo、price,一部分是自己定义的min_qty、discount。因为在派生类对象中含有与其基类对应的组成部分,所有我们能把派生类的对象当成基类对象来使用,而且我们也能将基类的指针或者引用绑定到派生类对象中的基类部分上。如下:

Quote item;        //基类对象
Bulk_quote bulk;   //派生类对象
Quote *p = &item;  //p指向Quote对象
p = &bulk;           //p指向bulk的Quote部分
Quote &r = bulk; //r绑定到bulk的Quote部分

注:在派生类对象中含有与其基类对应的组成部分,这一事实是继承的关键所在;并且每个类控制它自己的成员初始化过程,即派生类不能直接初始化基类的成员,虽然这些成员是从基类继承的,但是这些成员也必须使用基类的构造函数来初始化派生类的基类部分。 

基类中的静态成员

      如果基类中定义了一个静态成员,则在整个继承体系中只存在该成员的唯一定义。不论从基类中派生出多少个类,对于每个静态成员来说都只存在唯一的实例。对于静态成员的访问控制规则,则是和其他的成员一样,如果静态成员是private的,则派生类无权访问。如果静态成员是可以访问的,则我们既能通过基类使用它们,也可以通过派生类使用它。

派生类的声明

      派生类的声明与其他类差别不大,声明中包含类名,但是不包含他的派生列表,如:

class Bulk_quote : public Quote;   //错误:派生列表不能出现在这里
class Bulk_quote;                  //正确:声明派生类的正确方式

注:派生类的列表以及与定义有关的其他细节必须与类的主题一起出现。

被用作基类的类

     一个类可以被用来作为基类,那么首先该类必须已经定义了而非只是声明,而且一个类可以是基类,也可以是派生类,如:

class Base {..............};
class D1 : public Base {..............};
class D2 : D1 {........................};

  在上面的列子可以看到Base作为D1的基类,D1作为Base的派生类,而D1又作为D2的基类,D2作为D1的派生类。每个类都是继承直接基类的所有成员,对于一个最终的派生类来说,它会继承其直接基类的成员;该直接基类的成员又含有其基类的成员;依次类推直至继承链的顶端。因此,最终的派生类将包含它的直接基类的子对象以及每个基类的子对象。

注:如果我们想防止一个类被继承,那么可以在类名后跟一个关键final,如:

class NoDerived final {.............}
class Base {............}
class Last final : Base {...........} //Last不能作为基类
class Bad : NoDerived {..............} //错误:NoDerived不能做为基类
class Bad2 : Last {.................} //错误:Last不能作为基类

基类和派生类在对象之间不存在类型转换

     当我们用一个派生类对象为一个基类对象初始化或赋值时,只有该派生类对象中的基类部分会被拷贝、移动或赋值,它的派生类部分将被忽略掉。

如:

Bulk_quote bulk;       //派生类对象
Quote item(bulk);     //使用Quote::Quote(const Quote&)构造函数
item = bulk;             //调用Quote::operator=(const Quote&)

  当构造item时,运行Quote的拷贝函数。该函数只能处理bookNo和price两个(这两个是基类中的成员),它负责拷贝bulk中的Quote部分的成员,同事忽略掉bulk中Bulk_quote部分的成员。类似的,对于将bulk赋值给item的操作来说,只有bulk中Quote部分的成员被赋值给item。

原文地址:https://www.cnblogs.com/pengjun-shanghai/p/5286690.html