【问题标题】:Refactoring using Abstract Base Classes and Templates in C++在 C++ 中使用抽象基类和模板进行重构
【发布时间】:2017-01-11 09:41:46
【问题描述】:

我在尝试进行一些重构时遇到问题。我们有很多代码重复,我正在尝试解决这个问题。我有以下类结构

IMessageSink.h:

class IMessageSink
{
public:
    virtual ~IMessageSink() { };
    virtual void process(const taurus::Msg& msg) = 0;
};

我有以下所有模型必须继承的基类 ModelBase.h,此时请不要使用friend class EM:

class ModelBase : public virtual IMessageSink
{
public:
   ModelBase(Tag a);

   void process(const taurus::Msg& msg);
   void reset();

private:
   friend class EM; // I will ask about this below.

   virtual void calculate(double lambda) = 0;
};

friend EM 的实现不正确,我在下面问这个问题。然后我有一个从ModelBase,ModelM0.h实现/继承的类:

class ModelM0 : public virtual ModelBase
{
public:
    ModelM0(Tag a);

   static ModelM0* ModelM0::make(Tag a)
   {
      ModelM0* m = new ModelM0(a);
      m->reset();
      return m;
   }

private:
    void calculate(double lambda);
};

ModelM0.cpp 实现为:

ModelM0::ModelM0(Tag a) : ModelBase(a) { }

void ModelM0::calculate(double lambda)
{
    // Do stuff.
}

问题在于EM 朋友类以及如何以通用方式实现它。以前,此类仅适用于不继承自 ModelBase 的类型 ModelM0。现在其他模型也继承自 ModelBase 和 EM 也需要与这些一起工作 - 这就是问题所在。我在 EM.h 中有以下定义(我已将其更改为模板,因此我们可以指定我们使用的ModelBase 的类型TModel):

EM.h 为:

template <class TModel> 
class EM : public virtual IMessageSink
{
public:
    static EM* make(Tag a)
    {
        return new EM(a);
    }

    EM(Tag a);
    ~EM();

    void process(const taurus::Msg& msg);
    void run();
private:
    struct Bucket
    {
        TModel* _model;
        std::vector<TinyMatrix<1, 1> > _obs
    };

    EM::Bucket& getModel(int ag);
}

问题实现是EM::Bucket&amp; getModel(int ag);,在EM.cpp我们有

template<class TModel> 
EM<TModel>::EM(Tag a) { }

template<class TModel> 
EM<TModel>::~EM()
{
    run();
}

template<class TModel>
void EM<TModel>::process(const taurus::Msg& msg)
{
    int ag = getMessageCount(msg.type()); // External call.
    if (ag <= 3)
    {
        Bucket& b = getModel(ag);
        TModel* m = b._model;
        m->process(msg);
    }
}

上面好像还可以,我的问题是getModel的实现

template<class TModel> 
EM<TModel>::Bucket& EM<TModel>::getModel(int ag)
{
    // This is not right.
    TModel* m;
    m = TModel::make(getTag(ag)); // This is not right - I need a factory.

    // ... Do stuff.

    Bucket& b = // Get a bucket.
    b._model = m;

    return b;
}

我的问题:

  1. 如何更改上面的代码,以便在EM&lt;TModel&gt;::getModel(int ag) 中我可以使用上面的make 创建正确的TModel - 我需要一个工厂吗?这将如何实现?

  2. 在ModelBase.h 中,EM 类被指定为朋友类。我怎样才能使这个泛型与正在使用的TModel (ModelBase) 类型一起使用?

这里需要注意的是,这是一个重构问题,而不是我在方法中显示的代码是否正确或正确(为了简洁地突出我的问题,已将其删减)。重构是我唯一需要帮助的事情。非常感谢您的宝贵时间。

【问题讨论】:

  • 是的,因为代码无法编译。代码审查仅适用于工作示例...
  • do I need a factory and how would this be implemented? 工厂模式通常在创建的对象由通用部分组成时被考虑,这些通用部分可以耦合在一起构造给定的对象功能......
  • 请添加编译器错误。

标签: c++ templates inheritance refactoring class-design


【解决方案1】:

当我试图编译您的代码时,我必须修复一些缺少的分号和缺少的类型(Tag、taurus::Msg、TinyMatrix),并修复getModel(int ag) 的声明和定义

通常,您需要向编译器指出,Bucket 实际上是一个类型名称,而不是其他类型的参数。

对于声明,您有 2 个选项:

Bucket& getModel(int ag); // (1)
typename EM<TModel>::Bucket& getModel(int ag); // (2)

(1) 是您当前模板专业化的 Bucket 类型的隐式使用。 (2) 是显式类型使用以及编译器的typename 关键字,如上所述。

对于定义,你肯定需要typename 关键字,因为你不在你的类定义上下文中。

template<class TModel>
typename EM<TModel>::Bucket& EM<TModel>::getModel(int ag)
{
    // This is not right.
    TModel* m;
    m = TModel::make(getTag(ag)); // This is not right - I need a factory.

    // ... Do stuff.

    Bucket& b = // Get a bucket.
        b._model = m;

    return b;
}

忽略“这是不对的”。 cmets - 我从您的示例代码中复制了它们。它实际上完全正确。

对于friend 声明,您需要添加一个模板版本,因为您希望与所有可能的模板实例成为朋友。我是从this answer 查到的(感谢 Anycorn)

template <class> friend class EM;

希望能解决您的所有问题。注意我使用了template &lt;class&gt;,因为你使用了它。我个人更喜欢template &lt;typename&gt;。

【讨论】:

  • 我刚刚重新阅读了这个问题。要看friend 方面,我还没有考虑过。然而,大多数时候,需要friend 是潜在设计问题的指标。
  • 非常感谢您在这里的帮助和时间。我将实施您建议的更改并返回编译器错误...
  • 非常感谢您的宝贵时间。这已经成功了。代码可以编译,并像这次重大更改之前一样完美运行。你的时间是最感激的。再次感谢...
猜你喜欢
  • 2011-06-12
  • 2018-12-28
  • 2021-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-02
相关资源
最近更新 更多