分享

C 中广泛使用的技术之模板元编程让继承作为一种技术工具

 山峰云绕 2018-08-07

前言

C   中广泛使用的技术之模板元编程,让继承作为一种技术工具

      【【C语言基础】C ++中广泛使用的技术之模板元编程,让继承作为一种技术工具】https://toutiao.com/group/6586910869827895815/?iid=15906422033&app=explore_article&timestamp=1533652528&group_id=6586910869827895815&tt_from=copy_link&utm_source=copy_link&utm_medium=toutiao_ios&utm_campaign=client_share 




更多C/C 学习资料,请私信我“代码”,即可获取

C 是一种多范式语言,因此它不是纯粹的面向对象语言,而是具有其他方面,例如通过模板对通用编程的巨大支持。其主要优势之一是能够混合这些不同方面。

继承是C 中主要面向对象的方面之一,在纯粹的面向对象的上下文中,它意味着“Is-A”关系。在与C 的其他方面的混合中,继承可以用于纯粹的技术和其他原因,这些原因并不意味着面向对象的子类型。

在本文中,我将探讨在C 中继承的可能用途。

C   中广泛使用的技术之模板元编程,让继承作为一种技术工具

更多C/C 学习资料,请私信我“代码”,即可获取

面向对象

我将假设以面向对象的方式使用继承是充分已知的,因此我不会详细介绍Liskov替换原则和该主题的其他一般方面。但是有一些方面与C#或Java等其他语言不同,所以我将在这里简要介绍一下。

构造函数和析构函数中的虚函数

C   中广泛使用的技术之模板元编程,让继承作为一种技术工具

更多C/C 学习资料,请私信我“代码”,即可获取

我假设您了解虚拟功能以及它们的工作原理。然而,对于许多C 开发人员而言,在构造函数和析构函数中使用虚函数令人惊讶。请考虑以下代码:

C   中广泛使用的技术之模板元编程,让继承作为一种技术工具

更多C/C 学习资料,请私信我“代码”,即可获取

许多人会期望打印字符串“Derived :: foo”,因为它是一个“Derived”对象,它被创建。但是如果你知道编译器在构造对象时做了什么,很明显为什么不会发生这种情况:

构造`Derived`对象时,首先要构造该对象的`Base`部分。`Derived`对象尚未开始存在。作为“派生”对象的整个事物的身份在其所有部分(包括“Base”子对象已经初始化)之后得到建立。因此当调用`foo()`时,除了一个`Base`对象之外什么都没有,因此可以调用的唯一`foo`是`Base :: foo`。

同样的推理适用于析构函数中的虚函数调用:`Base`析构函数在`Derived`对象的所有其他部分都被销毁后作为最后一步执行,然后作为`Derived`的对象的身份已经结束。

虚拟析构函数

上面的`Base`类没有正确编写。根据 Herb Sutter的这条规则,“Base”的析构函数应该是虚拟的或受保护的。虚拟,如果你想能够在`Base`类指针上调用`delete`,包括使用`Base`智能指针,否则保护。

由于面向对象的继承通常与某种基类指针密切相关,因此将析构函数设置为public和virtual是正确的选择。因此,考虑到 全部规则或无规则,“基础”应如下所示:

C   中广泛使用的技术之模板元编程,让继承作为一种技术工具

更多C/C 学习资料,请私信我“代码”,即可获取

这看起来像是一个巨大的开销,但是在最顶层的基类中声明虚拟析构函数就足够了,任何派生类的析构函数也将自动虚拟化。

多重继承

除了允许类仅从单个其他类派生并且可能实现一个或多个接口的许多其他语言之外,C 允许实际的多重继承。这意味着,允许一个类派生自多个完全成长的类,每个类都有自己的成员变量,虚拟和非虚函数等等。

这可能会导致一些问题,其中一些问题非常微妙且违反直觉。例如,如果两个基类具有相同名称的函数,则在派生类中调用其中一个可能不明确。如果两个基类派生自同一个类,则形成“致命的死亡之钻”,这就是C 具有虚拟继承的原因。

继承作为一种技术工具

C   中广泛使用的技术之模板元编程,让继承作为一种技术工具

更多C/C 学习资料,请私信我“代码”,即可获取

当一个类派生自另一个类时,它不仅继承了成员变量和方法,还继承了该类的任何typedef和静态成员。这可以在不同的场合使用。

模板元编程

C 中广泛使用的技术,特别是库编写者,是模板元编程。它通常涉及只包含typedef和常量定义的小类。通常这些类永远不会被实例化,即没有创建它们的对象。

模板元编程中使用的许多类相互派生,以利用其基类中的常量和typedef的继承,而不必重新定义它们。这类的示例是模板` std :: integral_constant`。它的两个实例化,`std :: true_type`和`std :: false_type`它包含两个typedef和一个静态常量值,在这种情况下分别是`true`和`false`。

模板元函数的一个非常简短的示例是一个小模板,用于确定unsigned int是偶数还是奇数:

C   中广泛使用的技术之模板元编程,让继承作为一种技术工具

更多C/C 学习资料,请私信我“代码”,即可获取

从通用实施细节中获取

有时,有几个类共享其实现的一些常见细节。将实现分解为所有这些类使用的另一个类是正常的。通常的选择是使实用程序类的对象成为每个类的私有成员,但是实现者可能会选择从实用程序类派生而来:

  • 如果实用程序类提供了派生类'接口的许多方法,则派生类必须实现转发函数。
  • 如果实用程序类不是由派生类的实现者维护并包含受保护的函数,则实现者需要访问。
  • 空基类优化。如果实用程序类没有非静态成员且没有虚函数,则它不包含实际数据,即不需要占用任何空间。由于不同的原因,类的成员总是需要占用几个字节,但基类不需要它。因此,许多编译器优化了空基类占用的空间,使对象有效地变小。当然,只有在给出这种优化的需要时才应该使用它。(注意:请参阅下面的注释,如何在成员变量上使用EBO而不是类本身)。

奇怪的重复模板模式

所述CRTP是模板和继承在C 一起工作的另一个例子。这意味着一个类派生自一个仅使用派生类实例化的模板:

C   中广泛使用的技术之模板元编程,让继承作为一种技术工具

更多C/C 学习资料,请私信我“代码”,即可获取

使用此技巧的最常见场合之一是静态多态:基类可以调用派生类的方法,而无需为它们定义虚拟原型。这些方法的调用可以比虚函数调用快一点。但是,两个派生类没有公共基类,因为它们派生自模板的两个不同实例,它们是不同的类。

结论

在C 中有许多可以使用继承的方法,其中许多方法并不意味着面向对象的“Is-A”关系。通常可以通过最顶层基类中的公共虚拟析构函数来识别那些。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多