【问题标题】:C++: Can the parameter x of a pure virtual function f be replaced with a subtype of x in the derived classes implementation of f?C++:纯虚函数 f 的参数 x 可以在 f 的派生类实现中替换为 x 的子类型吗?
【发布时间】:2019-12-11 18:06:21
【问题描述】:

考虑以下抽象类,它将是一个类的接口,该类将某个对象携带的信息写入标准输出。

class FileBuilder
{
public:                                                                         
    virtual void build(const Object& object) = 0;

    virtual ~FileBuilder() = default;
};

此时我会注意到Object 也是一个抽象类,派生类SpecialObject。现在我要实现SpecialFileBuilder : FileBuilder,如下。

class SpecialFileBuilder : public FileBuilder
{
public:                                                                         
    void build(const SpecialObject& specialObject);
};

...

void SpecialFileBuilder::build(const SpecialObject& specialObject)
{
    // Do some stuff
}

我不完全理解为什么这是不可能的。 SpecialFileBuilder 尊重接口FileBuilder,并且在任何期望FileBuilder 的地方都可以改为SpecialFileBuilder。提前感谢您的帮助。

当然,如果我将内容更改为以下内容,这将起作用。

void SpecialFileBuilder::build(const Object& object)

但是,在我的SpecialFileBuilder::build() 实现中,我需要使用参数是SpecialObject,而不仅仅是Object

我应该如何处理这种设计?

【问题讨论】:

  • 该语言有一个非常简单的工具可以用来自己找出答案:override 说明符。只需用override 标记覆盖函数 - 如果它编译,那么你的答案是“是”,否则它是“否”。顺便说一句:我建议在应该覆盖基类函数的 all 函数上使用override。当您错过签名时,它有助于发现错误。
  • (+1) 好建议。在使用 C/C++ 之后,对于您提出的问题,我多次将此功能明确添加到 P.L.我正在设计。

标签: c++ oop inheritance abstract-class pure-virtual


【解决方案1】:

我不完全理解为什么这是不可能的。 SpecialFileBuilder 尊重 FileBuilder 接口,并且在任何需要 FileBuilder 的地方都可以被赋予一个 SpecialFileBuilder

您可能具有协变返回类型。

但对于参数,您需要contra-variant 返回类型(C++ 不支持)。

因为下面的代码应该是正确的

SpecialFileBuilder specialFileBuilder;
FileBuilder& fileBuilder;
SpecialObject2 specialObject2; // Other derived class, unrelated to SpecialObject
Object& object = specialObject2;

fileBuilder.build(object); // correct type
// but
specialFileBuilder.build(specialObject2); // won't compile

反变参数是

struct Base {
    virtual void f(const Cat&) = 0;
};

struct Derived : Base
{
    void f(const Animal&) override; // if contra-variance was supported
};

【讨论】:

    【解决方案2】:

    TL;DR 不,这没有任何意义。

    完整版如下。

    我不完全理解为什么这是不可能的。

    virtual void build(const Object& object) = 0;
    

    此声明是一个承诺。它承诺 build 可以接受 any Object 作为参数。此类承诺对派生类具有法律约束力,即它们必须实现基类所述的承诺。请注意,该声明并未承诺 build 可以接受某些对象,而不能接受其他对象。

    FileBuilder* builder = GetBuilder(); // we don't know what kind of builder it is
    
    SpecialObject some;
    builder->build(some); // must work
    
    OtherSpecialObject some;
    builder->build(other); // must work too
    
    UnrelatedObject whatever;
    builder->build(whatever); // must work as well
    

    现在看另一个声明

    void build(const SpecialObject& specialObject);
    

    它违背了承诺。最初的承诺是强大的。给我任何东西,我可以处理它。新的承诺很弱。哦,我是一个特殊的小建造者,我只能应付特殊的小物件!

    对不起,你不能用一个较弱的承诺来推翻一个强有力的承诺。如果你被允许,我们怎么能相信任何的承诺?

    现在如果你的设计不符合这个大纲,即你总是知道你得到什么样的构建器,并且你不想承诺处理各种各样的对象,那么你选择了一个错误的工具工作。也许您想尝试泛型编程。

    template <typename T>
    class FileBuilder {
       virtual void build (const T& t) = 0;
    };
    
    class SpecialBuilder:  public FileBuilder<SpecialObject> {
       void build (const SpecialObject& t) override;
    };
    

    现在上面的代码不起作用,我们需要修复它

    FileBuilder<SpecialObject>* builder = GetBuilder<SpecialObject>(); // we know exactly what we want to build
    
    SpecialObject some;
    builder->build(some); // will work;
    
    OtherSpecialObject other;
    builder->build(other); // sorry that's not in the contract, won't compile
    

    【讨论】:

      猜你喜欢
      • 2018-10-14
      • 2018-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-24
      相关资源
      最近更新 更多