问题标签 [non-virtual-interface]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
2 回答
609 浏览

c# - C# 中的非虚拟方法、静态绑定和接口

我知道非虚拟方法是静态绑定的,据我所知,这意味着它在编译时本身就知道将在哪个对象上调用哪个方法。该决定是根据对象的静态类型做出的。让我感到困惑的是接口(而不是class)和静态绑定。

考虑这段代码,

演示代码:http: //ideone.com/JOVmi

我明白了Line 1。编译器可以知道b.f()将调用它,B.f()因为它知道它的静态类型bB.

但是编译器如何在编译时自己决定ia.f()调用A.f()? 什么是对象的静态类型ia?不是IA吗?但那是一个接口,并且没有任何f(). 那么它是如何工作的呢?

为了使案例更令人费解,让我们考虑这种static方法:

正如评论所说,实现接口的类可能太多IA,那么编译如何静态决定ia.f()调用哪个方法?我的意思是,假设我有一个类定义为:

如您所见,C与 不同,除了派生于 之外,还B实现了. 这意味着,我们在这里有不同的行为:IAA

演示代码:http: //ideone.com/awCor

我如何理解所有这些变化,尤其是接口和静态绑定如何协同工作?

还有更多(ideone):

请帮助我理解所有这些,以及 C# 编译器如何完成静态绑定。

0 投票
1 回答
173 浏览

c++ - 使用 non-virtual-interface 成语,我的非虚拟函数可以/将被内联吗?

我最近决定使用非虚拟接口习惯用法 (NVI) 来设计 C++ 中的接口,主要是为了使用具有默认值的参数(从而避免由于默认参数是静态绑定的事实而引起的问题)。

我为我的班级带来了一个相当微不足道的声明,如下所示:

我知道在标头中提供函数体会自动将函数标记为内联候选函数(尽管我不知道将定义放在类之外是否会阻止这种情况)。我也知道虚函数没有内联,原因很明显(我们不知道在运行时会调用哪个函数,所以我们显然不能用函数体替换调用)。

那么,在这种情况下,是否会func()被标记为内联候选?不是虚函数,但仍然调用虚函数。它是否可以内联?

额外的问题:值得吗?正文只包含一个语句。

请注意,这个问题非常适合学习它,而不是到处搜索优化。我知道这个函数只会被调用几次(好吧,就目前而言,最好谨慎对待程序的发展方式),并且内联将是相当多余的,而不是我程序性能的主要关注点。

谢谢 !

0 投票
1 回答
110 浏览

inheritance - 私有继承和非虚拟接口

所以我对 D 感兴趣已经有一段时间了,前一段时间我把它搞砸了。我已经开始重新审视它,我真的很喜欢它试图实现的目标,但我对我最喜欢的一个 C++ 设计选项......非虚拟接口感到不安。

我喜欢这种设计的地方在于,它允许在继承层次结构的“顶部”进行前置和后置条件检查、日志记录和资源管理。这允许设计者指定一组相关类的所有通用功能,并将类的可定制部分分解为非常小的功能。它还减少了需要在子类中编写的功能量。另外,由于虚拟扩展点是私有的,它不会污染接口,或者允许用户直接调用特定于实现的函数(这真的很关键)。

有没有办法在 D 中实现这一点?

C++ 中的示例(未经测试,未编译......仅用于说明)。

0 投票
3 回答
303 浏览

c++ - 具有非虚拟接口和一些私有变量的克隆方法

我有一个抽象类Figure和一些派生类:Circle, Square, ...

类图实现:

以 Square 为例,这些数字的行为如下:

调用方法克隆时,我很难分配变量乘数。实现这一目标的最佳方法是什么?当然这只是一个愚蠢的例子,有很多变通方法,但实际上,有多个推导级别,它们并不那么明显,所以请坚持这个模式。


我是否应该为方法克隆也使用虚拟接口?通过这种方式,我可以直接在 Figure 类中分配乘数,而无需让每个图形知道其乘数。

0 投票
2 回答
142 浏览

c++ - 在非虚拟接口习语中添加不变量

假设我使用 NVI idiom 具有以下层次结构:

如果在层次结构中的某个点我想在非虚拟基础方法中“添加”不变量,那么最好的方法是什么?

一种方法是在 SpecialBase 级别递归 NVI 习语:

但我不太喜欢这个想法,因为我不想为我添加到我的层次结构中的每个派生基添加方法(具有不同的名称)......

另一种方法是拥有以下(不是NVI):

在我看来,这不那么令人困惑,因为在任何时候,一个具体的类只需要实现虚拟方法,而派生的基类可以覆盖基(虚拟)方法,如果它也选择的话。

是否有另一种更清洁的方法来实现相同的目标?

编辑:

我正在寻找一种非常通用的设计模式,它可以让我拥有以下类型的层次结构:

每个Base类都可以(并且将覆盖 foo),而类A-F只需要重新实现foo_impl.

请注意,仅添加另一个可选的自定义虚拟函数(例如bar_impl)在这里没有帮助,因为它只允许一个额外的自定义层,我可能需要一个无限的数量。

0 投票
2 回答
1219 浏览

java - Android活动生命周期:为什么不首先强制调用超级方法?

基本 Android 开发的要求之一(根据 Google 文档)是,当您覆盖 Activity 的生命周期方法(onCreate、onResume、onPause 等)时,必须首先调用父级的方法:

为什么 Android API 不使用非虚拟接口模式来强制执行此行为,而不是依靠开发人员记住这样做?:

Android 的 Activity Base 类可能看起来像这样(粗略的例子):

Android开发者编写的子类:

我还没有遇到过需要在调用 super 方法之前编写任何指令的情况。有没有什么时候我们不应该调用超级方法,或者不先调用它?如果对于生命周期中的任何特定方法,它确实必须始终处于首位,那么设计决定不使用 NVI 模式来执行它的原因是什么?

更新: 现在已经为 Android 开发了一段时间,工作中的每个人都使用我的 BaseActivity NVI 类,但我仍然没有遇到一个原因,不是对所有生命周期方法使用 NVI,而是 onCreate 方法。似乎那些为现有 API 设计辩护/评论的人并没有真正的理由,或者似乎并不真正理解 NVI 模式是什么,所以我假设没有好的原因,它“就是这样”。

0 投票
2 回答
148 浏览

c++ - 将非虚拟接口和多级继承结合在一起

Non-virtual Interface idiome (NVI) 非常不言自明:您不编写public virtual函数,而是编写public调用private virtual实现函数的函数,如下所示:

这使您(基类作者)能够检查和强制执行前置条件和后置条件或应用其他函数,以便派生类的作者不会忘记它们。

现在,当您是派生作者时,您可能想自己编写一个基类——我们称之为它Pawn——它扩展了 的功能,load()因此必须覆盖v_load(). 但是现在你面临一个问题:

当您覆盖时v_load(),其他想要从您的类派生的客户端将始终覆盖该行为,并且它们不能调用Pawn::v_load(),因为它是一个private函数,它们也不能调用,Pawn::load()因为它被定义为{ v_load; }其中Object当然会导致无限循环. 此外,当他们忘记那个电话时,要求他们这样做可能会导致错误。如果我希望他们启用它,我将不得不指定对v_load()as protectedin的访问权限Object,这似乎是一个丑陋的解决方案,因为它会Object大大削弱 的封装。

当然,您仍然可以覆盖v_load()以调用新函数v_pawnLoad(),然后由客户端覆盖,但这似乎很容易出错,因为许多客户端可能会重载错误的函数。

那么,我如何设计Pawn这样一种方式,即客户端仍然可以覆盖v_load(),同时保持检查前置条件或调用其他函数的能力,并且(如果可能)不启用,更不用说要求客户端ObjectPawn调用基本v_load()实现了?

0 投票
2 回答
2022 浏览

c++ - NVI成语下,虚函数为什么不能公开?

C++ 私有和受保护的虚拟方法以及不使用公共虚拟方法是否有任何正当理由?正在谈论非虚拟接口(NVI)和非公共虚拟功能及其共生关系。Scott Meyers 在 Effective C++ 中也说过

有时虚函数甚至必须是公共的,但 NVI 习语不能真正应用。

我没有看到的是为什么 NVI要求实现特定的虚拟功能是非公开的?从 Herb Sutter 的文章Virtuality中,它说这是一个很好的实践,例如,将公共(客户端)接口与实现细节(非公共接口)分开是很好的。我想知道的是,如果将此类虚函数声明为公共,我是否错过了任何语义上阻止应用 NVI 的语言功能?

例如:

如果我把SetStateBoolSetStateInt放在类定义的公共部分会有什么影响?

0 投票
0 回答
222 浏览

c++ - 是否可以编写一个自动包装和虚拟化非虚拟接口的 c++ 类模板?

问题

在编写单元测试时,通常需要模拟对象。为了使生产对象可替换,模拟对象类从生产对象类派生并覆盖一些虚函数。

当生产类没有虚函数并且您无法更改它时,就会出现问题。现在我看到两个选择来解决这个问题。

  1. 将您的类转换为由子系统类型参数化的类模板。您的生产类将MyClass<ProductionSubsystem>用于测试将使用MyClass<MockSubsystem>

  2. 手动编写一个带有虚函数的包装器,调用被包装子系统类的非虚函数。然后模拟包装器。

我对这两种选择中的任何一种都不完全满意。1 迫使我将我的“简单”类变成类模板,2 迫使我编写大量样板代码。

所以我想知道是否可以自动化为非虚拟类编写包装器的过程。我想象这样的事情:

此设置应允许以下用例:

问题

是否可以在 C++ 中实现 AutoWrapper 和 AutoWrapperImpl 这两个类?如果是,它是如何完成的,是否有公开的解决方案?

0 投票
0 回答
101 浏览

c++ - 多级 NVI 的虚拟模板解决方法

我正在尝试构建一个类,该类将充当我想要在我正在做的私人项目中序列化的任何类型的基类。

我试图通过为“<<”和“>>”提供功能来使该类至少与提升序列化档案和 QDataStream 一起工作。任何其他适用于该课程的流只是一个奖励。

重要提示:我可能只会使用 QDataStream。我将这门课更多地作为一个拼图/学习机会(这似乎有效),所以虽然我会欣赏完全偏离这种形式的解决方法,但如果事情能按照我希望的方式工作,我会非常喜欢(当然,考虑到语言的限制,尽可能接近),在途中获得一些知识。

认为的课程是:

我立即发现不允许使用虚拟模板,并且遇到了(对我而言)新术语“类型擦除”。

阅读这篇文章后,我尝试使用类型擦除:论 C++ 中面向对象和泛型编程之间的张力以及类型擦除可以做什么(直到并包括“超越 boost::any”)...

不成功。

几点注意事项:

Serializable_save 和 Serializable_load 是命名约定的一部分,它遵循继承并允许多级 NVI。 (多级 NVI 只是我为完成从基类继承的虚函数并为继承者提供新虚函数的概念而起的一个名称。允许一组动作始终沿着继承链发生)意味着不是最终类的继承类将如下所示:

我的下一个尝试是将“存档”模板埋在 StreamWrapper 类中(类似于类型擦除,但不完全是),以消除对模板的直接需求并通过编译器关于虚拟模板的无限问题。

当然,那没有用。因为我最终需要指定与我试图实现的完全相反的模板类型。

如果重要的话,我正在使用 C++14(我的意思是在 11 到 14 之间)。

我仍然认为类型擦除是答案。我只是不明白如何使用它。

所以,

  • 是否有可能实现我想要的行为?如果是:
  • 当虚拟模板非法时,如何实现允许这种“多级 NVI”行为的类?

编辑: 我认为可能是一个解决方案,但我还不能正确测试它。