• EffectiveC++-条款41:了解隐式接口和编译器多态


    一. 内容

    1. 面向对象的世界总是以显式接口和运行期多态解决问题。举个 Widget 的例子:

      class Widget {
      public:
          virtual ~Widget()=default;
          virtual void Normalize()=0;
      };
      class SpecialWidget:public Widget {
      public:
          virtual void Normalize() override {
              std::cout<<"Special"<<"\n";
          }
      };
      class NormalWidget:public Widget {
      public:
          virtual void Normalize() override {
              std::cout<<"Normal"<<"\n";
          }
      };
      inline void BeginPlayWithWidget(Widget& InWidget) {
          InWidget.Normalize();
      }
      inline void Try() {
          SpecialWidget SpecialWidget;
          NormalWidget NormalWidget;
          BeginPlayWithWidget(SpecialWidget); //Special
          BeginPlayWithWidget(NormalWidget);	//Normal
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
    2. 你可以这样说 BeingPlayWithWidget 函数中的 InWidget

      • 由于 InWidget 类型被声明为 Widget,所以 InWidget 必须支持 Widget 接口。我们可以在源码中找到这个接口,看看它们是什么样子,所以我们称之为一个显式接口(explicit interface),也就是它在源码中的确可见
      • 由于 Widget 的 BeingPlayWithWidget 函数是 virtual ,InWidget 将对此函数的调用表现出运行期多态(runtime polymorphism),也就是说将在运行期根据 InWidget 的动态类型决定究竟调用哪个函数。

      这就是显式接口运行期多态

    3. Templates及泛型编程的世界,与面向对象的世界有根本的不同。在此世界显式接口和运行期多态仍然存在,但重要性降低。举个例子:

      template <typename T>
      inline void BeginPlayWithWidgetTemplate(T& InT) {
          if (InT.Size() > 0 && InT != EClassType::None) {
              InT.Normalize();
              std::cout << InT.Size() << "\n";
              //...
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      现在我们怎么描述 InT 呢?

      • InT 必须支持哪一种接口,是由函数体中对 InT 的操作决定的。从本例来看,InT 的类型 T 必须要支持 Normalize ,Size等操作。看表面可能并非完全正确,但这组操作对于 T 类型的参数来说,是一定要支持的隐式接口(implicit interface)。
      • 凡是涉及 InT 的任何函数调用,例如 operator> 和 operator != 有可能造成 template 的具现化,使得这些调用得以成功,这样的行为发生在编译器。以不同的 template 参数具现化 function templates 会导致调用不同的函数,这便是所谓的编译期多态(compile-time polymorphism)。

      这就是隐式接口编译期多态

    4. 通常显式接口由函数的签名式:函数名称,参数类型,返回类型,常量性构成。隐式接口就不同了,它不基于函数签名式,而是由有效表达式(valid expression)构成。再次看看这个例子:

      template <typename T>
      inline void BeginPlayWithWidgetTemplate(T& InT) {
          if (InT.Size() > 0 && InT != EClassType::None) {
              //...
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      看样子 T 类型的隐式接口有这些约束:T 必须提供名叫 Size 的成员函数,该函数返回一个整数值;T 必须支持 operator!= 函数,用来与 EClassType 比较。

      感谢于操作符重载(operator overloading)带来的可能性,这两个约束都不需要被满足。第一个函数其实只需返回一个类型为 X 的对象,而这个对象可以与 0 一起调用 operator> 函数即可,第二个函数也不一定要求参数类型为 T,任何可被转换为成 operator!= 调用的对象即可。

      更复杂一点,我们甚至可以考虑 operator&& 被重载的情况,或许是某些另外行为完全不同的东西。

    二. 总结

    1. classes 和 templates 都支持接口(interfaces)和多态(polymorphism)。
    2. 对 classes 而言接口是显式的(explicit),以函数签名为中心。多态则是通过 virtual 函数发生于运行期。
    3. 对 template 参数而言,接口是隐式的(implicit),奠基于有效表达式。多态则是通过 template 具现化和函数重载解析(function overloading resolution)发生于编译期。
  • 相关阅读:
    死磕sparkSQL源码之TreeNode
    中南林业科技大学Java实验报告十二:数据库系统设计 - 从0到1搭建java可视化学生管理系统源代码
    python毕业设计作品基于django框架 电影院购票选座系统毕设成品(8)毕业设计论文模板
    Rust之包,箱和模块管理(三):引用模块树中项目的路径
    电路布线问题动态规划详解(做题思路)
    Unity --- 面板的使用与常用UI组件
    限时开源,来自大佬汇总的Kafka限量笔记,绝对不会后悔!
    使用html画一个键盘
    【PIE-Engine 数据资源】8天合成LAI产品(MOD15A2H.006)
    普冉PY32F071单片机简单介绍,QFN64 48封装,支持 8 * 36 / 4 * 40 LCD
  • 原文地址:https://blog.csdn.net/m0_51819222/article/details/125904578