【问题标题】:Achieving Interface functionality in C++在 C++ 中实现接口功能
【发布时间】:2010-11-11 05:23:20
【问题描述】:

我使用 OOP 的一个重要原因是创建易于重用的代码。为此,Java 风格的接口是完美的。但是,在处理 C++ 时,我真的无法实现任何类型的功能,例如接口……至少不是很容易。

我知道纯虚拟基类,但真正让我感到震惊的是,它们强迫我编写带有指针的非常尴尬的代码。例如。 map<int, Node*> nodes;(其中 Node 是虚拟基类)。

这有时没问题,但有时指向基类的指针并不是一个可行的解决方案。例如。如果要返回打包为接口的对象,则必须返回指向该对象的基类转换指针。但是该对象在堆栈上,并且在返回指针后将不存在。当然,您可以开始广泛使用堆来避免这种情况,但这会增加太多的工作量(避免内存泄漏)。

有没有什么方法可以在 C++ 中实现类似接口的功能而不必笨拙地处理指针和堆? (老实说,对于所有的麻烦和尴尬 id,而只是坚持使用 C。)

【问题讨论】:

  • 处理接口时根本不需要强制转换... Base *p = new Derived;完全有效。
  • 这里乱七八糟的,我不知道真正的问题是什么。输入您真正的问题的代码,我们将解释您做错了什么。在 Java 中可以做的所有事情都可以在 C++ 中完成(在这个方向上没有任何限制)。您的问题可能在于 Java 和 C++ 之间的风格差异,现在两者之间的移动是如此不同。
  • 这不是一个单一的编码问题,而是关于如何优雅地在c++中实现java风格接口的功能。事实上,我的整个帖子中有一个问题(最后,如果你读了它),剩下的就是阐述(不是漫无目的)。我不知道为什么你不明白,其他人做到了,并且已经提供了帮助。 (因此它有一个公认的答案。)

标签: c++ oop memory pointers interface


【解决方案1】:

这实际上是 C++ 大放异彩的案例之一。 C++ 提供了未绑定到类的模板和函数这一事实使得重用比在纯面向对象语言中容易得多。但现实情况是,您必须调整编写代码的方式才能利用这些优势。来自纯 OO 语言的人通常对此有困难,但在 C++ 中,对象接口不包括成员函数。事实上,在 C++ 中,尽可能使用非成员函数来实现对象接口被认为是一种很好的做法。一旦你掌握了使用模板非成员函数来实现接口的窍门,那将是一种改变生活的体验。 \

【讨论】:

    【解决方案2】:

    虽然 auto_ptr 有一些您必须知道的奇怪使用规则*,但它的存在是为了让这种事情轻松工作。

    auto_ptr<Base> getMeAThing() {
        return new Derived();
    }
    
    
    void something() {
        auto_ptr<Base> myThing = getMeAThing();
        myThing->foo();  // Calls Derived::foo, if virtual
        // The Derived object will be deleted on exit to this function.
    }
    

    *永远不要将 auto_ptrs 放在容器中,例如。了解他们在作业中所做的工作是另一回事。

    【讨论】:

    • std::auto_ptr 不能用于标准容器,因此不能用于替换问题代码中的原始指针。你需要一个 boost::shared_ptr 或 std::tr1::shared_ptr
    【解决方案3】:

    您可以使用 boost::shared_ptr 来避免原始指针。作为旁注,您在 Java 语法中看不到指针的原因与 C++ 如何实现接口与 Java 如何实现接口无关,而是因为 Java 中的所有对象都是隐式指针(* 被隐藏)。

    【讨论】:

      【解决方案4】:

      我认为您的问题的答案是否定的 - 没有更简单的方法。如果你想要纯接口(好吧,就像你可以在 C++ 中得到的一样纯),你将不得不忍受所有的堆管理(或尝试使用垃圾收集器。关于该主题还有其他问题,但我的对此问题的看法是,如果您想要一个垃圾收集器,请使用一种为垃圾收集器设计的语言。比如 Java)。

      在某种程度上减轻堆管理痛苦的一个重要方法是自动指针。 Boost 有一个很好的自动指针,可以为你做很多堆管理工作。 std::auto_ptr 有效,但在我看来它很古怪。

      您还可以评估您是否真的需要这些纯接口。有时你会这样做,但有时(就像我使用的一些代码一样),纯接口仅由一个类实例化,因此只是额外的工作,对最终产品没有任何好处。

      【讨论】:

      • Java 对于您计划反复运行的程序来说太慢了。我现在使用 c++ 的主要原因是扩展 NS2(网络模拟器),所以使用 boost 真的不是一种选择。不过,我将研究提升自动指针以供将来使用。至于我是否真的需要接口,答案是否定的。我不需要它们。就像我不需要 OOP 一样。但是接口确实使代码更易于重用:-p 但我知道最好不要白费力气地让自己经历这么多麻烦。
      • boost shared_ptr(或其近似克隆)正在成为新标准的一部分,并且作为库的tr1extension 的一部分可用于许多新编译器:std::tr1::shared_ptr&lt;Type&gt;
      【解决方案5】:

      模板元编程是一个很酷的东西。基本思路? “编译时多态性和隐式接口”,Effective C++。基本上你可以通过模板类获得你想要的接口。一个非常简单的例子:

      template <class T>
      bool foo( const T& _object )
      {
          if ( _object != _someStupidObject && _object > 0 )
              return true;
          return false;
      }
      

      那么在上面的代码中,我们可以对对象 T 说些什么呢?那么它必须与 '_someStupidObject' 兼容,或者它必须可转换为兼容的类型。它必须与整数值可比较,或再次可转换为 is 的类型。所以我们现在已经为类 T 定义了一个接口。“Effective C++”一书提供了更好和更详细的解释。希望上面的代码能让您对模板的“接口”功能有所了解。还可以查看几乎所有的 boost 库,它们几乎都充满了模板化。

      【讨论】:

      • 虽然我真的不明白你的例子是如何导致像接口这样的东西的,但我会确保看看那本书(尤其是那一章):)
      • 好的,我读了那部分,我明白你的目的。这种东西可能很有用。我想一个人必须在模板中留下一个带有隐式接口要求的注释才能真正有用(并避免让人们阅读所有代码)
      • 确实,你会的。人们做了一些很酷的事情,boost::spirit 解析器库就是一个很好的例子。
      • 另外请注意,我不一定在技术(即Java)定义方面谈论接口,我什至不确定这是什么。但对我来说,接口是定义对象必须遵守的一组要求的通用方式。 IE。将多态派生类型和纯虚基类函数,我们要求任何派生对象都必须有一定的接口。不过,这只是我的想法,我再次得到任何技术定义。
      • 顺便说一句,这不是真正的模板元编程,它只是使用模板。模板元编程使用模板作为图灵完备的功能语言,在编译时进行评估。
      【解决方案6】:

      考虑到 C++ 不需要generic parameter constraints like C#,那么如果你能摆脱它,你可以使用boost::concept_check。当然,这仅在有限的情况下有效,但如果您可以将它用作您的解决方案,那么您肯定会有更快的代码和更小的对象(更少的 vtable 开销)。

      使用 vtable(例如,纯虚拟基)的动态调度将使您的对象在实现更多接口时变得越来越大。 Managed languages do not suffer from this problem (this is a .NET link, but Java is similar).

      【讨论】:

        猜你喜欢
        • 2010-12-18
        • 1970-01-01
        • 2023-03-07
        • 1970-01-01
        • 1970-01-01
        • 2013-02-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-03
        相关资源
        最近更新 更多