【问题标题】:Choosing which base class to override method of选择要覆盖哪个基类的方法
【发布时间】:2016-11-11 07:42:50
【问题描述】:

鉴于以下情况:

class Observer
{
    public:
        virtual void Observe(Parameter p) = 0;
};

template<size_t Tag>
class TaggedObserver : public Observer { };

class Thing : public TaggedObserver<0>, TaggedObserver<1>
{
    public:
        virtual void Observe(Parameter p) override;
};

Thing::Observe 覆盖 TaggedObserver&lt;0&gt;::ObserveTaggedObserver&lt;1&gt;::Observe
有没有办法为每个基类提供不同的覆盖?
基本原理:我希望该类能够观察两个相同类型的通知源,每个源具有不同的操作,而不必诉诸在参数中传递源然后在if/switch 中检查它。

【问题讨论】:

  • TaggedObserver&lt;0&gt;TaggedObserver&lt;1&gt; 是同一个类。为什么不创建两个子类?
  • @JimV:他们不是。它们被模板化的目的是区分基类。
  • @JimV: TaggedObserver&lt;0&gt;TaggedObserver&lt;1&gt; 可能基于相同的模板 类,但它们是不同的实例化 类。例如,就像 std::vector&lt;int&gt;std::vector&lt;char&gt; 是不同的类一样。模板参数值是类类型的一部分,会产生不同的 RTTI 结果。
  • 我有一个想法,但到目前为止它只产生了一个新问题:case of template member function specialization that works on msvc, not others
  • P.S. FWIW 发布了一个替代解决方案 here 作为我相关问题的答案。就像下面@skypjack 的回答一样,它使用 CRTP 将基础 Observe“重新映射”为模板化的 Thing::Observe&lt;N&gt;

标签: c++ inheritance multiple-inheritance observer-pattern


【解决方案1】:

为了提供不同的覆盖,需要定义不同的派生类,例如:

class Observer
{
public:
    virtual void Observe(Parameter p) = 0;
};

template<size_t Tag>
class TaggedObserver : public Observer
{
};

class TaggedObserverZero : public TaggedObserver<0>
{
public:
    virtual void Observe(Parameter p)
    {
        // do something ...
    }
};

class TaggedObserverOne : public TaggedObserver<1>
{
public:
    virtual void Observe(Parameter p)
    {
        // do something else ...
    }
};

但是,如果您希望 Thing::Observe() 首先接收 Parameter 并将其分派到适当的基类,则无法避免在 Thing 中使用 if 语句(或等效语句),因为它继承TaggedObserver::Observe() 的多个副本,需要决定调用哪一个:

class Thing : public Observer, TaggedObserverZero, TaggedObserverOne
{
public:
    virtual void Observe(Parameter p)
    {
        if (some condition)
            TaggedObserverZero::Observe(p);
        else if (some other condition)
            TaggedObserverOne::Observe(p);
    }
};

或者,你可以无条件地给他们俩打电话,让他​​们自己想办法:

class TaggedObserverZero : public TaggedObserver<0>
{
public:
    virtual void Observe(Parameter p)
    {
        if (some condition)
            // do something ...
    }
};

class TaggedObserverOne : public TaggedObserver<1>
{
public:
    virtual void Observe(Parameter p)
    {
        if (some other condition)
            // do something else ...
    }
};

class Thing : public Observer, TaggedObserverZero, TaggedObserverOne
{
public:
    virtual void Observe(Parameter p)
    {
        TaggedObserverZero::Observe(p);
        TaggedObserverOne::Observe(p);
    }
};

【讨论】:

    【解决方案2】:

    TaggedObserver 中实现它们(如果需要,提供明确的专业化),例如:

    class Observer {
    public:
        virtual void Observe(Parameter p) = 0;
    };
    
    template<size_t Tag>
    class TaggedObserver : public Observer {
    public:
        void Observe(Parameter p) override { }
    };
    
    template<std::size_t... I>
    class Thing : public TaggedObserver<I>... {
    public:
        Thing(): TaggedObserver<I>{}... {}
    
        template<std::size_t N>
        void Observe(Parameter p) {
            TaggedObserver<N>::Observe(p);
        }
    };
    

    然后,您可以将Thing 特化为Thing&lt;0, 1&gt;,并使用thing.Observe&lt;1&gt;(p) 调用正确的函数。

    编辑

    此编辑的目的是展示一个新的示例代码,即使稍作修改,也或多或少与上述代码相同。
    我希望这可以帮助OP。其基本思想是将CRTP习语、虚方法和继承结合起来。

    class Observer {
    public:
        virtual void Observe(Parameter p) = 0;
    };
    
    template<template T, size_t Tag>
    class TaggedObserver : public Observer {
    public:
        void Observe(Parameter p) override {
            T *t = static_cast<T*>(this);
            // Now use whatever you want from T, that is Thing in this example
        }
    };
    
    template<std::size_t... I>
    class Thing : public TaggedObserver<Thing<I...>, I>... {
         template<std::size_t J>
         friend class TaggedObserver<Thing<I...>, J>;
    
    public:
        Thing(): TaggedObserver<Thing<I...>, I>{}... {}
    
        template<std::size_t N>
        void Observe(Parameter p) {
            TaggedObserver<Thing<I...>, N>::Observe(p);
        }
    };
    

    注意friend 声明允许TaggedObservers 访问Thing 的私有成员。

    这样,TaggedObservers 中的Observe 的实现可以根据 cmets 中的要求访问来自Thing 的公共、受保护和私有成员。

    最后你可以根据需要专门化TaggedObserver,从而为Observe提供不同的实现。
    举个例子:

    template<template T, size_t Tag>
    class TaggedObserver;
    
    template<template T>
    class TaggedObserver<T, 0>: public Observer {
    public:
        void Observe(Parameter p) override {
            T *t = static_cast<T*>(this);
            // Now use whatever you want from T, that is Thing in this example
            // Put here the code of the specialization for Tag 0
        }
    };
    
    template<template T>
    class TaggedObserver<T, 1>: public Observer {
    public:
        void Observe(Parameter p) override {
            T *t = static_cast<T*>(this);
            // Now use whatever you want from T, that is Thing in this example
            // Put here the code of the specialization for Tag 1
        }
    };
    

    【讨论】:

    • 不错。有没有办法将template&lt;std::size_t N&gt; void Observe(Parameter p) 移到更高的地方,因为不需要在每个Thing-like 类中复制它?这就是我在case of template member function specialization that works on msvc, not others 尝试做的,但没有找到正确的咒语。
    • 我的意图是不能调用不同的TaggedObserver::Observe 方法,这已经可以使用thing-&gt;TaggedObserver&lt;N&gt;::Observe。我的意图是 Thing 将为每个 TaggedObserver::Observe 提供不同的覆盖
    • @Dani 为什么不能在TaggedObserver 中定义它们?这样,Thing 将继承这些特化。也许我误解了这个问题。
    • @skypjack:因为他们需要一些Thing 成员
    • @Dani 答案已更新。让我知道这是否对您有帮助。
    猜你喜欢
    • 2013-11-05
    • 2012-09-15
    • 2011-12-23
    • 1970-01-01
    • 2017-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-14
    相关资源
    最近更新 更多