Effective C++读书笔记:条款34

Aki 发布于 2022-10-03 234 次阅读


34区分接口继承和实现继承:

1、接口继承和实现继承表示的意义

  接口继承:父类提供的接口,子类仍然提供。

  实现继承:子类复用父类的代码实现。

2、子类继承父类,可分为下列情况:

  a、继承接口,继承实现---对应non-virtual方法。

  b、继承接口,继承缺省实现,允许子类修改实现---对应virtual方法。

  c、只继承接口---对应pure virtual也就是纯虚函数方法,父类只提供接口,强制子类提供实现。

  b、关闭接口,继承实现:private继承,表示根据某物实现出,子类关闭接口

三种继承对应的成员函数的写法

(1)只继承接口

对应的成员函数的写法为:pure virtual纯虚函数。
特点:此类继承派生类必须自己写实现,父类会成为抽象类,无法实例化对象。
注意:pure virtual 函数是可以写定义的,但是只能通过类名调用。

(2)同时继承接口和实现,且继承而来的实现能够被覆写

对应的成员函数的写法:virtual 。
特点:此类继承派生类可以缺省实现,缺省的话,就会继承基类的实现。也可以自己手动写实现,这样相当于是覆盖了基类的实现,是一种动态多态。

(3)同时继承接口和实现,且继承而来的实现不能够被覆写

对应的成员函数的写法:non-virtual。
特点:此类继承,派生类必须继承缺省和实现,且不能够修改。

class Base 
{
public:
    virtual void func() = 0;    //父类提供了接口
    virtual void func1(){}      //父类提供了虚函数也实现了
    void func2(){}              //父类的普通函数

private:
    virtual void func3(){}      //父类私有函数
};

class Derived : public Base 
{
public:
    
    void func()override{}      //子类继承接口并实现
    void func1()override {}   //子类继承虚函数,也可以修改实现          
    using Base::func2;        //直接继承父类的func2()
                              //子类关闭了接口func3()
};

现在,考虑下面的场景:父类有个virtual方法XXX(),目的是让子类继承缺省实现,允许子类修改。现在有个子类,应该重写XXX,但是程序员忘了,会出现什么结果?

  编译不会出错,运行时调用父类的缺省实现。这有可能会导致大灾难。有没有办法预防呢? 这个问题的关键是,接口和缺省实现在一起。解决办法是:父类使用pure virtual方法,强制要求子类提供实现,如果子类不提供,编译通不过。同时,父类定义一个non-virtual方法,提供缺省实现。子类重写pure virtual方法时,可以转交父类提供的缺省实现。

这样解决的缺点是:多声明了一个缺省实现的方法。有没有更好的办法呢?

  在C++,对于pure virtual方法可以提供实现。因此,可以在父类中,定义pure virtual方法,子类重写pure virtual,转交父类的pure virtual实现。

使用pure virtual 方法代替virtual方法的目的是:为了防止子类忘记重写,分离接口和实现,强制子类重写。其实,这个理由并不好,因为有时候,子类确实想继承缺省实现,使用了pure virtual,必须重写,在方法内转交缺省实现。

另外,使用pure virtual方法,会使父类成为抽象类,不能实例化。

请记住:

接口继承和实现继承不同。在public继承下,derived class总是继承base class 的接口。

pure virtual函数只具体指定接口继承

简朴的(非纯)impure virtual函数具体指定接口继承及缺省实现继承

non-virtual函数具体指定接口继承以及强制性实现继承