【问题标题】:C++ cyclic dependency using class templates - how to refactor?使用类模板的 C++ 循环依赖 - 如何重构?
【发布时间】:2012-09-01 10:21:38
【问题描述】:

我正在处理 C++ 中的循环依赖问题。

情况如下:

libA.so:
    - Body.cpp
    - Header.cpp
    - DataObject.cpp
    - DataObject::read(boost::asio::streambuf* data)
      {
          boost::asio::streambuf data;

          ....

          body = (new DataConverter<Body>)->convert(&data);
          header = (new DataConverter<Header>)->convert(&data);
      }

  libB.so:
      - DataConverter.cpp
          -> DataConverter<T>
          -> T* DataConverter<T>::convert(boost::asio::streambuf* data)

  libA.so <-> libB.so

存在循环依赖,因为 libA 使用来自 libB 的 Converter-Class,而 libB 现在需要了解需要转换的 libA 的 Object-Types - 因为 DataConverter::convert 返回一个 Body 或 Header 对象。

我曾考虑通过前向声明来解决这个问题 - 但这对我来说似乎不是最干净的解决方案。总而言之,我的计划是提供一个可扩展的 DataConverter 解决方案。

你们认为最佳做法是什么?也欢迎完全不同的设计:)

最好, 塞巴斯蒂安

【问题讨论】:

  • 对我来说这看起来像是内存泄漏——你几乎肯定想要DataConverter&lt;Body&gt;().convert(&amp;data),没有new(除非convertdelete this 结尾,但这太可怕了)。跨度>
  • 这是比其他任何东西都更多的伪代码:)

标签: c++ templates circular-dependency extensible


【解决方案1】:

如果您需要一个类模板DataConverter,那么它不能是任何已编译库的一部分。它必须通过包含文件可用。一旦你把DataConverter 代码放在一个标题中,libAlibB 都使用你的循环依赖就消失了。

【讨论】:

    【解决方案2】:

    您的设计似乎有缺陷。如果名为 A 和 B 的两个库相互依赖,则意味着它们必须始终一起交付。如果它们必须始终一起交付,则意味着它们在逻辑上是同一接口的一部分。这意味着实际上,您只有一个库。

    没有足够的信息来说明最佳解决方案,但这里有一些提示:

    1. 合并这些库。
    2. 使一个库依赖于另一个库,例如,将 DataConverter 移至 libA。
    3. 创建一个实用程序库,依赖于这两个。
    4. 使用模板或虚拟类在 libB 中创建适当的接口,并使 libA 依赖于 libB。后者(虚拟类)很可能是动态链接库中更好的选择。

    【讨论】:

      【解决方案3】:

      您可能希望创建定义接口的抽象基类并相互隐藏实现(派生类)。

      【讨论】:

      • 好的 - 定义一个 AbstractConverter Factory 怎么样,它实际上返回指定模板化目标格式的具体实现。但这将如何解决我的依赖问题?
      • 定义一个包含转换器需要的所有方法(抽象)的抽象类。您可以在头文件中执行此操作。在 liba.so 和 libb.so 的源代码中都包含这个头文件。 libb 中的转换器(实际上,可能)从抽象类派生。 libb 包含两个返回转换器的工厂方法,这些工厂方法在 liba 中被声明为 extern。因此,libb 知道 liba,但 liba 现在不知道 libb。
      【解决方案4】:

      一些替代方案:

      1. DataConverter 作为一个完全通用的实现,将在编译时以适当的类型在 libA.so 中实例化。这是一个典型的 c++-ish 解决方案。您来自 libA(或其他)的“可转换”类型必须满足 Convertable 的完全模板化实现将使用的一些 DataConverter 概念

      2. 依赖倒置,由 JohnB 提出。您基本上可以实现相同的目标,但在运行时使用接口、实现和注册/解析。还有很多工作要做,但可扩展、可实现 ABI、可部署为库等...

      3. 两者的巧妙结合,例如Boost.Serialization。然而,这很难实现,也很容易打破......

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-07
        • 2012-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多