C ++:聚合,继承和指针

时间:2014-03-27 22:07:57

标签: c++ pointers inheritance aggregation

这是关于给定问题的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 ++中使用它的正确方法吗?或者也许存在一些其他的,更简单的,更灵活的或更少的无问题的"这种可互换的子功能实现的模式?在这种情况下使用指针是不可避免的吗?

1 个答案:

答案 0 :(得分:0)

对我来说很合理。如果你能确信数据源和选项源在算法的生命周期中是活的,我认为指针很好。我不认为指针是不可避免的。这里有一些其他选项(其中一些是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和/或GetDataChunkconst吗?应该打电话给他们更改源?如果它们可以是const,则意味着算法只需要一个const指针/对源的引用,这可能会使查找错误更容易一些。您谈到重用源对象,如果算法可以对源对象进行更改,这可能并不明智。例如,如果你反复调用GetDataChunk会发生什么,你每次都得到一个不同的块吗?如果您可以在不同的算法中使用相同的数据源,则可能会产生意外后果。

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