自引用传递及设计模式 (下)

  上一篇 打引用传递至设计模式
(上) 的文末,提到非虚拟接口 NVI
的实现,即将虚函数声明也保护型或私有型,借由模板函数模式来兑现 。

  园友 @KillU 看之大细心,提出了一个题目:虚函数是 private
类型,继承可以吗? 答案是:可以

5  实现权和调用权

  <Effective C++> 中于的解释是: 重写一个虚函数,指的凡哪些做作业
(how), 而调用一个虚函数,指的凡什么时候召开业务 (when)

  NVI 或 模板函数模式受到,允许派生类更写虚函数,赋给派生类的凡“实现权” –
即函数功能的贯彻;但是基类仍然控制“调用权” – 即什么时调用虚函数

 
听起特别是生硬,来拘禁一个实例,还是模板函数模式的落实,只不过是将虚函数声明也民用的
(private)

5.1  私有虚函数

  基类 AbstractClass 和 派生类 ConcreteClass

class AbstractClass {
public:
    void TemplateMethod();
private:
    virtual void PrimitiveOperation1();
    virtual void PrimitiveOperation2();
};

class ConcreteClass : public AbstractClass {
private:
    void PrimitiveOperation1() override;
    void PrimitiveOperation2() override;
};

  基类 AbstractClass 中,两独虚函数 PrimitiveOperation1,PrimitiveOperation2 以及 TemplateMethod
的落实

// implementation of private member function
void AbstractClass::PrimitiveOperation1() { std::cout << "Operation1 from AbstractClass !" << std::endl; }
void AbstractClass::PrimitiveOperation2() { std::cout << "Operation2 from AbstractClass !" << std::endl; }

// implementation of non-virtual member function
void AbstractClass::TemplateMethod() 
{
    PrimitiveOperation1();
    PrimitiveOperation2();
}

  派生类 ConcreteClass 中,重写少单虚函数 PrimitiveOperation1 以及 PrimitiveOperation2

// override
void ConcreteClass::PrimitiveOperation1() { std::cout << "Operation1 from ConcreteClass !" << std::endl; }
void ConcreteClass::PrimitiveOperation2() { std::cout << "Operation2 from ConcreteClass !" << std::endl; }

 
当通过指针或引用调用虚函数时,具体是调用基类还是派生类里的虚函数,取决于动态绑定到拖欠指针或引用的类似对象

// call virtual functions in AbstractClass
AbstractClass* p1 = new AbstractClass;
p1->TemplateMethod();

// call virtual functions in ConcretetClass
AbstractClass* p2 = new ConcreteClass;
p2->TemplateMethod();

  输出的结果如下:

Operation1 from AbstractClass !
Operation2 from AbstractClass !
Operation1 from ConcreteClass !
Operation2 from ConcreteClass !

 
这个结果,乍一收押并无见面这发疑问,因为派生类更写了零星单虚函数,这半单虚函数也是派生类自己之个体成员函数,调用自己类里的虚函数自然没什么问题。

5.2  虚函数不深受再写

  下面改变一下先后,派生类 ConcreteClass
内无另行写点儿个虚函数,只是独自的接轨自基类 AbstractClass,如下所示:

class ConcreteClass : public AbstractClass { };

  执行顺序,输出结果:

Operation1 from AbstractClass !
Operation2 from AbstractClass !
Operation1 from AbstractClass !
Operation2 from AbstractClass !

  这时候疑问便应运而生了:怎么派生类 ConcreteClass 居然可以看基类
AbstractClass 的私虚函数? 这可是私类型

 
其实就是如出一辙栽错觉,并无是派生类直接调用了基类的民用虚函数,而是派生类的非虚成员函数
ConcreteClass::TemplateMethod ,

  因为累自基类的非虚成员函数
AbstractClass::TemplateMethod,从而间接的调用了基类的村办虚函数。

  实际的“调用权”依然牢牢掌握在基类手中,只不过是基类提供了一个
TempaltMethod 的接口 (interface),可以让着生类来间接调用而就

5.3  私有非虚函数

  再次修改程序,基类内 PrimitiveOperation1 和 PrimitiveOperation2
不再声明也虚函数,也就算基类从派生类手中收回了函数的“实现权”

class AbstractClass {
public:
    void TemplateMethod();
private:
    void PrimitiveOperation1(); // non-virtual
    void PrimitiveOperation2(); // non-virtual
};

  同时派生类 ConcreteClass 中,声明自己的私成员函数
PrimitiveOperation1 及 PrimitiveOperation1,“隐藏”了基类中对应的同名函数

class ConcreteClass : public AbstractClass {
private:
    void PrimitiveOperation1();
    void PrimitiveOperation2();
};

  执行顺序输出结果:

Operation1 from AbstractClass !
Operation2 from AbstractClass !
Operation1 from AbstractClass !
Operation2 from AbstractClass !

  输出的结果与 5.2 是一模一样的,派生类调用
ConcreteClass::TemplateMethod,而 ConcreteClass::TemplateMethod 继承自
AbstractClass::TemplateMethod,

 
因此,实际上,仍然要基类内之非虚成员函数,调用基类内的私有成员函数

5.4  纯虚函数

  重申 5.1
中的同一句子话:当通过指针或引用调用虚函数时,具体调用基类还是派生类里的虚函数,取决于动态绑定到该指针或引用的类对象基类里之个人成员函数

 
可以这样理解,虚函数提到的是动态绑定,和其自己是公有、私有还是保护,并随便多老大关系

  实际被利用被,既然要以模板方法模式,那就是说必定使在派生类
ConcreteClass 中更写 PrimitiveOperation1
及 PrimitiveOperation2

 
而而保管这有限单函数一定会被派生类更写,可以用她声明也纯虚函数,即当函数最末尾加
“= 0”,如下所示:

class AbstractClass {
public:
    void TemplateMethod();
private:
    virtual void PrimitiveOperation1() = 0;
    virtual void PrimitiveOperation2() = 0;
};

  这,因为基类内说明了纯虚函数,所有基类 AbstractClass
变成了一个空洞基类(abstarct base class)

  抽象基类C++只能提供接口 (interface),而勿可知实例化,
执行下代码是会错的:

AbstractClass* p1 = new AbstractClass;  // error !
p1->TemplateMethod();

 

小结:

1)  NVI
gives the derived class control over how functionality is
implemented, but the base class reserves the right when the function
will be called

2)  Derived
classes may redefine private inherited virtual
functions

3)  Pure
virtual functions specify inheritance of interface only

 

参考资料:

 <Effective C++> item 35

 <C++
Primer_5th> 15.4 Abstract Base Classes

 <Design
Patterns> Template Method