【问题标题】:Best way to use a C++ Interface使用 C++ 接口的最佳方式
【发布时间】:2010-11-14 22:41:28
【问题描述】:

我有一个类似的接口类:

class IInterface
{
public:

    virtual ~IInterface() {}

    virtual methodA() = 0;

    virtual methodB() = 0;

};

然后我实现接口:

class AImplementation : public IInterface
{
    // etc... implementation here
}

当我在应用程序中使用接口时,最好创建具体类 AImplementation 的实例。例如。

int main()
{
    AImplementation* ai = new AIImplementation();
}

或者最好在接口中放置一个工厂“创建”成员函数,如下所示:

class IInterface
{
public:

    virtual ~IInterface() {}

    static std::tr1::shared_ptr<IInterface> create(); // implementation in .cpp
    virtual methodA() = 0;

    virtual methodB() = 0;

};

然后我就可以像这样在 main 中使用界面了:

int main()
{
    std::tr1::shared_ptr<IInterface> test(IInterface::create());
}

第一个选项似乎是常见的做法(不是说它是正确的)。但是,第二个选项来自“Effective C++”。

【问题讨论】:

  • 语言正确性的几点说明:在 C++ 中没有方法,只有成员函数。
  • 我会这样写 main():int main() { IInterface* i = new AIImplementation(); }

标签: c++ interface


【解决方案1】:

使用接口的最常见原因之一是您可以“针对抽象进行编程”而不是具体实现。

这样做的最大好处是它允许更改部分代码,同时最大限度地减少对剩余代码的更改。

因此,尽管我们不知道您正在构建什么的完整背景,但我会选择接口/工厂方法。

话虽如此,在较小的应用程序或原型中,我经常从具体的类开始,直到我知道在哪里/是否需要接口。接口可以引入一定程度的间接性,这对于您正在构建的应用程序的规模来说可能不是必需的。

由于应用程序较小,我发现我实际上并不需要自己的自定义界面。与许多事情一样,您需要根据具体情况权衡成本和收益。

【讨论】:

  • 谢谢阿什。我最终选择了这个解决方案,因为它在声明和定义之间提供了清晰的界限。因此我只需要在使用接口时包含接口头文件,而不是包含实现的头文件。
【解决方案2】:

还有一个你没有提到的替代方案:

int main(int argc, char* argv[]) { //... boost::shared_ptr 测试(新的 AImplementation); //... 返回0; }

换句话说,可以使用智能指针而不使用静态“创建”函数。我更喜欢这种方法,因为“创建”函数只会增加代码膨胀,而智能指针的好处是显而易见的。

【讨论】:

  • 如果实现应该是隐藏的,将被动态加载,依赖于平台(例如AWindowsImplementation,AMacImplementation,ALinuxImplementation),或者依赖于一些配置参数,那么你应该创建一个工厂。但是,恕我直言,您应该为此使用单独的工厂类。
【解决方案3】:

您的问题中有两个不同的问题: 1.如何管理创建对象的存储。 2.如何创建对象。

第 1 部分很简单 - 您应该使用像 std::tr1::shared_ptr 这样的智能指针来防止内存泄漏,否则需要花哨的 try/catch 逻辑。

第 2 部分更复杂。

你不能像你想要的那样在 main() 中编写 create() - 你必须编写 IInterface::create(),否则编译器将寻找一个名为 create 的全局函数,它是不是你想要的。使用 create() 返回的值初始化“std::tr1::shared_ptr 测试”似乎可以满足您的需求,但这不是 C++ 编译器的工作方式。

关于在接口上使用工厂方法是否比仅使用 new AImplementation() 更好的方法,它可能对您的情况有所帮助,但要注意推测的复杂性 - 如果您正在编写接口,因此它总是创建一个 AImplementation 而不是 BImplementation 或 CImplementation,很难看出额外的复杂性会给你带来什么。

【讨论】:

    【解决方案4】:

    “更好”在什么意义上?

    如果您只打算拥有一个具体的类,工厂方法不会给您带来太多好处。 (但话又说回来,如果你只打算有一个具体的类,你真的需要接口类吗?也许是的,如果你使用的是 COM。)无论如何,如果你能预见到一个小的、固定的限制具体类的数量,那么从整体上看,更简单的实现可能是“更好”的实现。

    但是如果可能有很多具体类,并且如果您不想让基类与它们紧密耦合,那么工厂模式可能会很有用。

    是的,这可以帮助减少耦合——如果基类为派生类向基类注册自己提供了一些方法。这将允许工厂知道存在哪些派生类,以及如何创建它们,而不需要关于它们的编译时信息。

    【讨论】:

    • 我实际上正在使用 COM。我有一个 VCL GUI,我需要从 Windows 资源管理器中拖放。因此,我在我的界面中继承了 IDropTarget,并且我正在获取希望成为应用程序中放置目标的组件,以通过此界面注册它们的句柄。因此,使用工厂方法可能不值得,最好使用上面 Micahel Safyan 的建议。
    【解决方案5】:

    使用第一种方法。您在第二个选项中的工厂方法必须按具体类实现,这在接口中是不可能的。即,IInterface::create() 不知道您实际希望实例化哪个具体类。

    静态方法不能是虚拟的,在这种情况下,在您的具体类中实现非静态 create() 方法并没有真正为您赢得任何东西。

    工厂方法当然有用,但这不是正确的用法。

    Effective C++ 中的哪个项目推荐第二个选项?我没有在我的书里看到它(虽然我也没有第二本书)。这可能会消除一个误解。

    【讨论】:

    • 我手边没有我的 Effective C++,但我认为 OP 指的是抽象工厂模式(我一直认为这是矫枉过正,但也许有时它是合适的。)作为是否需要为每个具体类实现工厂:您将有一个公共工厂方法,它调用非公共静态方法来创建具体实例。派生类将自己注册到工厂以使自己可用于构造。工厂如何选择在给定时间构建哪个派生类是特定于实现的。
    • 但您对 create() 方法的看法是正确的。在书中它实际上是 test(IInterface::create())
    【解决方案6】:

    我会选择第一个选项,因为它更常见且更易于理解。这完全取决于您,但如果您在开发商业应用程序,那么我会问我的同行他们使用什么。

    【讨论】:

      【解决方案7】:

      我确实有一个非常简单的问题:

      您确定要使用指针吗?

      这个问题可能看起来不合逻辑,但是具有 Java 背景的人使用 new 的频率比要求的要高得多。在您的示例中,在堆栈上创建变量就足够了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-18
        • 2010-10-23
        • 2011-09-24
        • 2012-12-28
        • 1970-01-01
        • 2013-08-12
        • 1970-01-01
        • 2018-06-20
        相关资源
        最近更新 更多