继承和虚函数与泛型编程

Inheritance & virtual functions Vs Generic Programming

本文关键字:泛型编程 函数 继承      更新时间:2023-10-16

我需要明白,是否真的Inheritance & virtual functions在c++中没有必要,一个人可以实现一切使用Generic programming。这来自Alexander Stepanov和我正在观看的讲座是Alexander Stepanov: STL及其设计原则

我总是喜欢将模板和继承视为两个正交的概念,在非常字面的意义上:对我来说,继承是"垂直"的,从顶部的基类开始,然后"向下"到越来越多的派生类。每个(公开的)派生类就其接口而言都是基类:poodle是a dog是a animal。

另一方面,模板是"水平的":模板的每个实例具有相同的形式代码内容,但是两个不同的实例是完全独立的,不相关的部分,以"并行"方式运行,彼此看不到。对整数数组进行排序在形式上与对浮点数数组进行排序相同,但整数数组与浮点数数组完全没有关系。

由于这两个概念是完全正交的,它们的应用也是。当然,您可以设计出一种情况,在这种情况下,您可以相互替换,但是如果按照习惯做法,模板(泛型)编程和继承(多态)编程都是独立的技术,它们都有自己的位置。

继承是通过添加细节使抽象概念变得越来越具体。泛型编程本质上是代码生成

作为我最喜欢的例子,让我来提一下这两种技术是如何在类型消除的流行实现中完美地结合在一起的:单个处理程序类持有抽象容器类的私有多态基指针,而具体的派生容器类则由模板化的类型推导构造函数确定。我们使用模板代码生成来创建任意的派生类族:

// internal helper base
class TEBase { /* ... */ };
// internal helper derived TEMPLATE class (unbounded family!)
template <typename T> class TEImpl : public TEBase { /* ... */ }
// single public interface class
class TE
{
  TEBase * impl;
public:
  // "infinitely many" constructors:
  template <typename T> TE(const T & x) : impl(new TEImpl<T>(x)) { }
  // ...
};

它们有不同的用途。泛型编程(至少在c++中)是关于编译时多态的,而虚函数是关于运行时多态的。

如果具体类型的选择取决于用户的输入,那么你真的需要运行时多态性——模板帮不了你。

多态性(即动态绑定)对于基于运行时数据的决策至关重要。通用数据结构很好,但它们有局限性。

示例:考虑离散事件模拟器的事件处理程序:用纯虚函数实现它非常便宜(就编程工作而言),但是如果纯粹用模板化类实现,则非常冗长且相当不灵活。

作为经验法则:如果您发现自己在切换(或If -else-ing)某个输入对象的值,并根据其值执行不同的操作,那么可能存在一个更好的(在可维护性意义上)动态绑定解决方案。

前段时间我想过一个类似的问题,我只能梦想给你这样一个伟大的答案,我收到了。也许这是有帮助的:接口范例性能(动态绑定与泛型编程)

这似乎是一个非常学术的问题,就像生活中的大多数事情一样,有很多方法来做事情,在c++的情况下,你有很多方法来解决问题。没有必要对事物持异或态度。

在理想情况下,您应该使用静态多态性模板,以便在类型不是由用户输入决定的情况下提供最佳性能。

实际情况是模板将你的大部分代码强制放入头文件中,这导致了编译时间的激增。

我已经做了一些繁重的泛型编程利用静态多态性实现一个泛型RPC库(https://github.com/bytemaster/mace (rpc_static_poly分支))。在这种情况下,协议(JSON-RPC,传输(TCP/UDP/Stream/etc)和类型)在编译时都是已知的,所以没有理由做虚表调度…或者有吗?

当我通过预处理器运行单个。cpp的代码时,结果是250,000行,编译一个对象文件需要30秒以上的时间。我在Java和c#中实现了"相同"的功能,它在一秒钟内就编译好了。

几乎你包含的每个stl或boost头文件都会增加成千上万行代码,这些代码必须对每个对象文件进行处理,其中大部分是冗余的。

编译时间重要吗?在大多数情况下,它们对最终产品的影响比"最大限度地优化桌面消除"更大。原因是每个"bug"都需要一个"尝试修复,编译,测试"的周期,如果每个周期需要30秒以上的时间,开发就会变得缓慢(注意Google go语言的动机)。

在花了几天时间学习java和c#之后,我决定我需要"重新思考"我学习c++的方法。c++程序没有理由比实现相同功能的底层C程序编译得慢得多。

我现在选择运行时多态性,除非分析显示瓶颈在虚表调度中。我现在使用模板在处理void*或抽象基类的底层对象之上提供"即时"多态性和类型安全接口。通过这种方式,用户不需要从我的"接口"派生,仍然有泛型编程的"感觉",但他们可以获得快速编译时间的好处。如果性能成为一个问题,那么可以用静态多态性替换泛型代码。

结果是惊人的,编译时间从30多秒下降到大约1秒。后预处理器源代码现在只有几千行,而不是25万行。

在讨论的另一边,我正在为一组相似但略有不同的嵌入式设备开发一个"驱动程序"库。在这种情况下,嵌入式设备几乎没有空间容纳"额外代码",也没有使用"虚表"调度。在C语言中,我们唯一的选择是通过函数指针"分离目标文件"或运行时"多态性"。使用泛型编程和静态多态,我们能够创建可维护的软件,运行速度比我们用c语言生产的任何软件都要快。