【问题标题】:C++: aggregation, inheritance and pointersC++:聚合、继承和指针
【发布时间】:2014-05-07 04:07:27
【问题描述】:

这是一个关于给定问题的 C++ 中特定 OO 实现的问题:

算法有几种变体,比如说两种,它们都有一些通用部分。这些算法(它们的实现,AlgImpl)接收启动选项。他们还接收输入数据,并逐个处理这些数据。有不同的方式可以提供启动选项(来自文件、来自网络、来自用户输入),以及接收数据的不同方式(来自文件、来自设备)。对于不同的数据源,有一个通用的 API,对于选项也是如此。

架构应该允许使用任何可用的 AlgImpl 以及任何可用的选项和数据源。它还应该允许添加新的 AlgImpl 和可能的新类型的选项和数据源,而对原始代码的更改最少或不更改(比如说只有两个 AlgImpl、两个选项源类型和两个数据源类型)。

这是我对 C++ 的看法,使用继承、聚合和指针。由于所有 AlgImpl 都共享一些公共部分,因此很自然地围绕一个基本抽象类来组织它们,因此(省略非必要的数据类型)我们有:

class BaseAlgorithm
{
   ...  //Abstract class with common code
};

class SimpleAlgorithmImpl: public BaseAlgorithm
{
   ...
};

class OptimalAlgorithmImpl: public BaseAlgorithm
{
   ...
};

现在,选项和数据源分别具有相同的界面:

class BaseOptionsSource
{
public:
   //Interface
   virtual void GetOptions(Options& opts) = 0;
};

class FileOptionsSource: public BaseOptionsSource
{
   void GetOptions(Options& opts);
   ...
};

class NetworkOptionsSource: public BaseOptionsSource
{
   void GetOptions(Options& opts);
   ...
};

class BaseDataSource
{
public:
   //Interface
   virtual void GetDataChunk(DataChunk& chunk) = 0;
};

class FileDataSource: public BaseDataSource
{
   void GetDataChunk(DataChunk& chunk);
   ...
};

class DeviceDataSource: public BaseDataSource
{
   void GetDataChunk(DataChunk& chunk);
   ...
};

然后将选项和数据源设为 BaseAlgorithm 的成员:

class BaseAlgorithm
{
public:
   BaseAlgorithm(BaseDataSource* pDataSrc, BaseOptionsSource* pOptsSrc); 
   BaseDataSource* _pDataSrc;
   BaseOptionsSource* _pOptsSrc;
};

BaseAlgorithm::BaseAlgorithm(BaseDataSource* pDataSrc, BaseOptionsSource* pOptsSrc):
      _pDataSrc(pDataSrc), _pOptsSrc(pOptsSrc)
{   
}

然后可以按如下方式创建特定的算法对象:

DeviceDataSource dataSrc;
NetworkOptionsSource optsSrc;
SimpleAlgorithmImpl simpleAlg(&dataSrc, &optsSrc);

对于新的 AlgImpl 或新类型的源,其类应实现继承的方法。当然,必须有一个“if/else if/...”代码,在创建 AlgImpl 对象之前明确选择使用的 AlgImpl 和源的集合。

在这种情况下,源对象也可以被重用,前提是 AlgImpl 对象不管理传递给它的源对象的分配/解除分配。

您认为这是在 C++ 中执行此操作的正确方法吗?或者,对于这种可互换的子功能实现,是否存在其他更简单、更灵活或更少“无问题”的模式?这种情况下是不是就不能避免使用指针了?

【问题讨论】:

    标签: c++ pointers inheritance aggregation


    【解决方案1】:

    在我看来是合理的。如果您可以确信数据源和选项源在算法的生命周期内是活跃的,我认为指针很好。我不认为指针是不可避免的。以下是一些其他选项(其中一些是 C++11):

    参考文献

    最好是 const 引用。如果您在构造函数中传递指针并且不需要它们可以为空,则可能比指针更安全。

    BaseAlgorithm(BaseDataSource& dataSrc,
                  BaseOptionsSource& optsSrc) :
        dataSrc_(dataSrc),
        optsSrc_(optsSrc) {}
    

    共享智能指针

    如果您无法确定数据源或选项源在算法的整个生命周期内都处于活动状态,请考虑传入 std::shared_ptrstd::weak_ptr。如果您想让他们活着,请使用std::shared_ptr,如果您想知道他们是否还活着,请使用std::weak_ptr

    BaseAlgorithm(std::weak_ptr<BaseDataSource> dataSrc,
                  std::weak_ptr<BaseOptionsSource> optsSrc) :
        dataSrc_(dataSrc), 
        optsSrc(optsSrc) {}
    

    下沉

    这取决于这些数据源、选项源和算法是如何创建和使用的,但如果算法获得数据源和/或选项源的所有权并通过 unique_ptr 传递,可能会使所有权变得更简单。

    BaseAlgorithm(std::unique_ptr<BaseDataSource> dataSrc,
                  std::unique_ptr<BaseOptionsSource> optsSrc) :
        dataSrc_(std::move(dataSrc)),
        optsSrc(std::move(optsSrc)){} 
    

    一些小问题:我质疑 out 参数是否是获取选项和数据块的最佳方式。即使对于像数据块这样大的东西也不要害怕按值返回任何现代编译器都会优化副本,它使调用代码更易于阅读和编写:

    Options opts = optsSrc_->GetOptions(opts);
    DataChunk data = dataSrc_->GetDataChunk(data);
    

    您可能错过了它们以简化您的帖子,但我想知道GetOptions 和/或GetDataChunk 是否可以是const?是否应该调用它们来更改源?如果它们可以是const,则意味着算法只需要一个指向源的 const 指针/引用,它可能会使查找错误稍微容易一些。您谈论重用源对象,如果算法可以对源对象进行更改,这可能是不明智的。例如,如果你反复调用 GetDataChunk 会发生什么,你每次都得到不同的块吗?如果您可以在不同的算法中使用相同的数据源,这可能会产生意想不到的后果。

    最后,确保 _pOptsSrc 和 _pDataSrc 是 protectedprivate 不公开。

    【讨论】:

    • GetOptions 和 GetDataChunk 通常不能为 const。选项源对象既用作最初获取选项的接口,也用作以后查询的存储(例如,如果我想要特定的选项值,我可以将 operator[](string opt_name) 添加到类中)。
    • GetOptions 可以通过将初始选项的获取移至构造函数来摆脱。但是 GetDataChunk 通常不能是 const,例如当从文件中读取数据时,必须保持文件中的块位置。重用源对象是一个附带的想法,这不是必需的。
    • @Student4K:很公平,所以选项归选项源所有?在这种情况下,GetOptions 可能应该返回 const Options&amp; 来表示,或者更好的是,按照您所说的去做并完全摆脱它并提供对特定选项的访问权限。
    • @Student4K:如果数据源不能(也不应该)共享,那么 Sink 成语在这种情况下是合适的。您可以使用 unique_ptr 将数据源的所有权传递给算法。只要您清楚所有权,指针就可以了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-08
    • 2014-08-09
    • 1970-01-01
    • 2010-09-08
    • 2013-06-13
    • 1970-01-01
    相关资源
    最近更新 更多