双可怕钻石继承问题(允许替代解决方案)

发布于 2025-01-14 13:15:11 字数 1379 浏览 3 评论 0 原文

我最终陷入了下面列出的情况。

我有一个库是纯 CPP,没有外部库,另一个项目是一个与外部库交互的 SDK。

双重可怕Diamond问题

名称前面的“I”表示抽象类。箭头表示继承。

我有 IDevice,其中包含 HandleInput(data) 作为回调,以及 StartDevice()。 然后我有一个更具体类型的设备:ISmartwatch(包含StartTimer),并从中继承了一个更具体的版本SmartwatchV1,它根据需要实现了HandleInput(data)。

这一切看起来都很棒,直到我来到外部 SDK 部分,该库希望我使用继承来与其交互以覆盖某些函数。因此,我必须从外部库和我自己的 CPP 库继承,以覆盖我需要的功能。这些库覆盖中的大多数适用于任何设备 (IExternalLibDevice),但少数特定于确切的秒表版本 (ExternalSmartWatchV1)。

然后,对于我的 SDK 中的多态性,我想调用和重写由库和我自己的设备示例提供的函数:libDevice.StartDevice(),并在此可选重写的 StartDevice 中使用库调用。或者stopWatch.StartTimer()、stopwatchV1.libraryOverride()。 我需要创建的对象是绿色对象,但是,白色 SmartWatchV1 也是在没有库的应用程序中实例化的对象。 (显然,我会记住任何未来的替代设备或秒表版本。)

我认为,如果我删除任何继承箭头,我要么会失去多态性(因此 SDK 代码仅适用于非常特定的智能手表版本),要么我不能覆盖我不再需要的功能。组合会很好,但不适用于覆盖函数,或者是否有我不知道的选项?

所以,我最终来到了这里。我在实现此方法时遇到了一些烦人的错误,因为双钻石通常是通过虚拟继承来解决的(关于双钻石的好页面:https://isocpp.org/wiki/faq/multiple-inheritance#mi-diamond)。然而,当在这里应用时(请参见图像中指示“虚拟”的 v),我有一个继承,该继承应该既是虚拟的又不是虚拟的。此外,虚拟继承使得构造函数在我的通用 CPP 库中变得非常烦人。即使没有 virtual (据我所知,这会导致内存中的一些类重复,并且需要解决很多歧义),我也会遇到一些构造函数错误(对于不能具有默认值的类,“没有合适的默认构造函数”构造函数等)问题。

我长期以来一直在努力解决这个问题,我希望更有经验的人可以提出建议,为我的代码结构或问题提供更好的解决方案。

I ended up in a situation lined out below.

I have one library that is pure CPP without external libraries, and another project that is an SDK to interface with an external library.

Double Dreadful Diamond issue

"I" in front of the name indicates an abstract class. Arrows indicate inheritance.

I have IDevice which contains HandleInput(data) as a callback, and StartDevice().
Then I have a more specific type of device: ISmartwatch (containing StartTimer), and from that inherits a more specific version SmartwatchV1, which implements HandleInput(data) according to its needs.

That all seemed great until I came to the external SDK part, where the library expects me to use inheritance to interface with it to override some functions. So, I have to inherit from the external library, and from my own CPP library, to override the functions I need. Most of these library overrides suit any device (IExternalLibDevice), but a few are specific to the exact Stopwatch version (ExternallSmartWatchV1).

Then for polymorphism in my SDK, I would like to call and override functions both provided by the library and my own device example: libDevice.StartDevice() and use library calls within this optionally overriden StartDevice. Or stopWatch.StartTimer(), stopwatchV1.libraryOverride().
The object which I need to create is the green one, however, the white SmartWatchV1 is also an object to instantiate in applications without the library. (And obviously I keep in mind any future alternative devices or stopwatch versions.)

I think if I drop any inheritance arrow, I would either lose out on polymorphism (so SDK code will only work for a very specific smartwatch version), or I cannot override functions I need anymore. Composition would be nice, but won't work for overriding functions, or is there an option I don't know about?

And so, I ended up here. I am encountering quite some annoying errors implementing this, since double diamond is usually solved with virtual inheritance (nice page about double diamond: https://isocpp.org/wiki/faq/multiple-inheritance#mi-diamond). However, when applied here (see the v's that indicate "virtual" in the image), I have one inheritance that should both be virtual and not be virtual. Additionally, virtual inheritance makes constructors really annoying in my generic CPP library. Even without virtual (which as far as I'm aware would cause some duplication of classes in memory and a lot of ambiguity to solve), I have some constructor errors ("no suitable default constructor" for a class that must not have a default constructor, etc) issues.

I have been battling to solve this for a long time, and I hope someone more experienced can make a suggestion that provides a better solution for my code structure or issue.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

西瓜 2025-01-21 13:15:11

最后,我通过使用组合解决了这个问题:

  • 将 IDevice 指针添加到构造函数中设置的 IExternalLibDevice 。
  • 在 IExternalLibSmartwatch 中:向构造函数添加 ISmartwatch 指针并将其传递给其父构造函数。另外,添加一个函数来检索 IDevice 指针作为 ISmartwatch。
  • 在ExternalSmartwatchV1中:还将SmartwatchV1添加到构造函数并将其传递给其父构造函数,并创建一个函数来检索IDevice指针作为SmartwatchV1。

IDevice 指针保存对 cppLibDevice 的引用,现在可以转换为它所属的任何子类。缺点:我无法覆盖 cpp lib 类,但这对我的代码来说并不是硬性要求,因为我在ExternalLib 类中创建了替代函数,这些函数可以选择调用 cppLibDevice 函数,或者完全替换它们。

In the end, I solved it by using composition:

  • Add an IDevice pointer to IExternalLibDevice that is set in the constructor.
  • In IExternalLibSmartwatch: add an ISmartwatch pointer to the constructor and pass it to its parent constructor. Also, add a function that retrieves the IDevice pointer as an ISmartwatch.
  • In ExternalSmartwatchV1: also add a SmartwatchV1 to the constructor and pass it to its parent constructor, and create a function that retrieves the IDevice pointer as a SmartwatchV1.

The IDevice pointer holds the reference to the cppLibDevice, and can now be cast to any of the subclasses it belongs to. Downside: I cannot override the cpp lib classes, but it was not a hard requirement for my code, since I created alternative functions in the ExternalLib classes that can optionally call the cppLibDevice functions, or completely replace them.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文