c++中使用抽象基类和模板进行重构

Refactoring using Abstract Base Classes and Templates in C++

本文关键字:重构 基类 抽象 c++      更新时间:2023-10-16

我在尝试重构时遇到了一个问题。我们有很多重复的代码,我正在努力解决这个问题。我有如下的类结构

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的类, modelm .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);
};

modelm .cpp实现为:

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

问题在于EM朋友类以及如何以通用的方式实现它。以前,这个类只处理没有从ModelBase继承的ModelM0类型。现在其他模型也继承了ModelBaseEM需要与这些工作-这就是问题所在。我在EM.h中有以下定义(我已经将其更改为模板,以便我们可以指定我们使用TModelModelBase的类型):

with 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& 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<TModel>::getModel(int ag)中我可以使用上面的make创建正确的TModel -我需要工厂吗?如何实现?

  2. ModelBase.h中,EM类被指定为友类。如何使我的通用工作与TModel (ModelBase)类型正在使用?

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

当我试图让你的代码编译时,我不得不修复一些缺失的分号和缺失的类型(Tag, taurus::Msg, TinyMatrix),还修复了getModel(int ag)的声明和定义

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

声明有两个选项:

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;
}

忽略"This is not right."注释-我从您的示例代码中复制了它们。这是完全正确的。

对于friend声明,您需要添加一个模板版本,因为您想要使所有可能的模板实例化成为朋友。我从这个答案中查找它(credit to Anycorn)

template <class> friend class EM;

希望这能解决你所有的问题。注意,因为您使用template <class>,所以我使用了它。我个人比较喜欢template <typename>