纯虚拟基础方法在派生类中的专业化

时间:2018-09-02 06:40:17

标签: c++ inheritance polymorphism pure-virtual

我想知道是否存在一种通用的方式/模式,以使派生类在给定的基类中具有某种更为专门化的纯虚拟方法版本。

class Base {
public:
    Base() = default;
    virtual ~Base() = 0;
    virtual void foo(int bar) = 0;
};
inline Base::~Base() {}

class Derived public Base {
public:
    Derived() = default;
    ~Derived() = default;
    void foo(int bar) override {/*...*/}
};

class SpecializedDerived : public Base {
public:
    SpecializedDerived() = default;
    ~SpecializedDerived() = default;
    void foo(int bar, double additionalParameter) override {/*...*/}
};

SpecializedDerived类中的重写是不可能的,因为该方法的签名与纯虚拟Base类中的签名不对应。

现在,有没有一种方法可以实现所描述的设计?由于存在类继承,因此可以实现“更专门的方法”吗?

在输入时,我认为我的愿望更多是“老兄,我只想让您提供某种iterate(.)函数!”东西。

到目前为止,我想到的唯一想法是

class Base {
public:
    Base() = default;
    virtual ~Base() = 0;
    virtual void foo(int bar) = 0;
};
inline Base::~Base() {}

class SpecializedDerived : public Base {
public:
    SpecializedDerived(double addParam) : additionalParam_(addParam) {}
    ~SpecializedDerived() = default;
    void foo(int bar) override {
        iterate(bar, additionalParam_);
        return;
    }
private:
    double additionalParam_;
    void foo(int bar, double additionalParam) {/*...*/}
};

内部函数调用实际上是多余的,就像您可以做的那样:

class SpecializedDerived : public Base {
public:
    SpecializedDerived(double addParam) : additionalParam_(addParam) {}
    ~SpecializedDerived() = default;
    void foo(int bar) override {/* code using additionalPara_ */}
private:
    double additionalParam_;
};

2 个答案:

答案 0 :(得分:1)

Wikipedia+ contravariance开始阅读。这个想法是孩子可以用接受更广泛接口的功能覆盖该功能。该函数必须接受旧接口,但也可以对其进行扩展。这个想法不是要破坏对基类有引用/指针的代码的期望。

请注意,当前C ++不支持逆变,因此这只是一个学术讨论。

您代码中的重写函数不接受对旧接口的调用,因此不算为矛盾。 覆盖:

virtual void foo(int bar):

覆盖:

void foo(int bar, double additionalParameter) override {/*...*/} };

正确的方法是在C ++中手动遵循矛盾原则。这或多或少是您想要做的。您需要覆盖:

void foo(int bar) override

并使其具有附加参数来调用该函数。对于类的用户来说,这似乎是矛盾(带有附加的默认参数)。

注意:使用常规函数重载虚拟函数是危险的,并且可能导致问题。重载函数会在父级中隐藏具有相似名称的函数。除非小心处理,否则可能导致混乱和错误。 override关键字使问题不太严重,但仍然存在一些风险。

答案 1 :(得分:1)

为什么需要matchnig签名?

多态性和虚函数背后的想法是,调用者不必知道有关其使用的对象的真实类的任何详细信息:

示例:

Base *my_object = find_dynamically_a_relevant_object (...);              
my_object->foo(10);   // could be a Derived or a SpecializedDerived

std::vector<Base*> container;  
...                   // somehow you populate the container with a mix of 
...                   // Derived AND SpecializedDerived objects 
for (auto x: container)
    x->foo(std::srand());  

这就是为什么签名必须与基类中定义的完全匹配的原因。

但是也可以使用其他签名吗?

现在,您可以很好地定义在三种情况下具有完全不同签名的重载foo()

  • 不是override:这是一个具有相同名称的不同功能。 *仅当您确定对象具有正确的类型时,才可以使用其附加参数调用重载的foo()。
  • 您必须确保提供了带有匹配签名的替代(因为它是纯虚拟的)。例如,这可能只是调用您的重载,对附加参数使用一些任意值)

示例:

class SpecializedDerived : public Base {
public:
    SpecializedDerived() = default;
    ~SpecializedDerived() = default;
    void foo(int bar) override { foo(bar, 0.0); }
    void foo(int bar, double additionalParameter)  {cout<<"specialized "<<bar<<" "<<additionalParameter<<endl;}
};

... // elsewhere, for example in main(): 

SpecializedDerived my_obj;  
my_obj.foo(10);  // use the override of the base
my_obj.foo(10, 37.2); // use the overload

// suppose that p is a Base* like in the first example
auto *q = dynamic_cast<SpecializedDerived*>(p); 
if (q)  // dynamic cast makes this nullptr if not the right type
    q->foo(10, 37.2); 
else cout << "Not specialized"<<endl; 

根据某些辅助数据替换行为

现在,如果您想在严格多态的上下文中使用foo()但仍具有一些(隐藏的)其他参数,则有几种可能性,例如:

  • 您可以扩展基本签名并添加具有默认值的其他(通常未使用)参数。在大多数情况下,这是一个坏主意:如果新的派生类带有另一个参数,该怎么办?
  • 您可以在进行调用之前注入其他参数(根据您自己的建议在构造时,或者在需要时使用设置器更改值)。适用于大多数情况。唯一的风险是在调用foo()
  • 之前确保正确设置了附加参数
  • 您可以更改签名以将包含所有实际参数的对象用作单个参数。这需要一些额外的开销,但是对于可变参数来说非常灵活。唯一的事情是,该对象的类型也很可能也需要是多态的,在这种情况下,必须确保正确的参数类型用于正确的对象。对我来说,这似乎超级有力,但超级有风险。