C++协方差何时是最佳解决方案?

29 c++ return-type covariance

这个问题在几个小时前就在这里提出,让我意识到我从未在自己的代码中实际使用过协变返回类型.对于那些不确定协方差是什么的人,它允许(通常)虚函数的返回类型不同,前提是类型是同一继承层次结构的一部分.例如:

struct A {
   virtual ~A();
   virtual A * f();
   ...
};

struct B : public A {
   virtual B * f();
   ...
};
Run Code Online (Sandbox Code Playgroud)

两个f()函数的不同返回类型被认为是协变的.较旧版本的C++要求返回类型相同,因此B必须如下所示:

struct B : public A {
   virtual A * f();
   ...
};
Run Code Online (Sandbox Code Playgroud)

所以,我的问题是:有没有人有一个真实世界的例子,其中需要协变返回类型的虚函数,或者产生一个优秀的解决方案来简单地返回一个基指针或引用?

Mar*_*wis 31

规范示例是.clone()/ .copy()方法.所以你总能做到

 obj = obj->copy();
Run Code Online (Sandbox Code Playgroud)

无论obj的类型是什么.

编辑:此克隆方法将在Object基类中定义(因为它实际上是在Java中).因此,如果clone不是协变的,你要么必须强制转换,要么限制为root基类的方法(与副本的源对象的类相比,它只有很少的方法).

  • 除了协方差外,该类型是静态检查的. (6认同)
  • 国际海事组织,任何形式的演员阵容都是丑陋和愚蠢的,真的应该尽可能地避免.使用dynamic_cast,没有编译时检查B :: clone确实执行方法名称所暗示的并返回B*(而不是指向A的其他子类的指针). (3认同)
  • 但你不应该(恕我直言)关心类型是什么A*a = some_a_or_b-> clone(); 也可以正常工作,然后在克隆指针上使用其他虚方法. (2认同)
  • 我将协变更多地视为语法糖,然后将其视为所需的功能。你总是可以做 A* a = b->clone(); Dynamic_cast<B*>( a )->initB(); (2认同)
  • 这个例子对Java的残缺类型系统有意义,但我仍然没有看到C++中的相关性. (2认同)

Ste*_*sop 12

通常,协方差允许您在派生类接口中表达比基类接口中更多的信息.派生类的行为比基类的行为更具体,协方差表示(差异的一个方面).

当您拥有相关的gubbins层次结构时,在某些客户端希望使用基类接口但其他客户端将使用派生类接口的情况下,它很有用.省略const-correctness:

class URI { /* stuff */ };

class HttpAddress : public URI {
    bool hasQueryParam(string);
    string &getQueryParam(string);
};

class Resource {
    virtual URI &getIdentifier();
};

class WebPage : public Resource {
    virtual HttpAddress &getIdentifier();
};
Run Code Online (Sandbox Code Playgroud)

知道他们有WebPage(也许是浏览器)的客户知道查看查询参数是有意义的.使用资源基类的客户端不知道这样的事情.它们将始终将返回HttpAddress&URI&变量绑定到变量或临时变量.

如果他们怀疑,但不知道他们的Resource对象有一个HttpAddress,那么他们可以dynamic_cast.但是,协方差优于"只知道"和执行演员,原因与静态输入完全相同.

还有其他选择 - 坚持使用该getQueryParam功能URIhasQueryParam对所有内容都返回false(使URI接口混乱).保留WebPage::getIdentifier定义为返回URL&,实际返回一个HttpIdentifier&,并让调用者做一个无意义的dynamic_cast(使调用代码和WebPage的文档混乱,你说"返回的URL保证可以动态地转换为HttpAddress").添加一个getHttpIdentifier函数WebPage(使WebPage界面混乱).或者只是使用协方差来表达它的意图,这表明a WebPage没有a FtpAddress或a MailtoAddress,它有一个HttpAddress.

最后,当然有一个合理的论点,你不应该有gubbins的层次结构,更不用说gubbins的相关层次结构了.但是这些类可以很容易地与纯虚方法接口,所以我认为它不会影响使用协方差的有效性.

  • 格宾斯?这不是我熟悉的一个词.谷歌搜索它从UrbanDictionary.com产生这个:gubbin是有时候像流浪汉一样的人."你不相信他的父母会拥有一座豪宅吗?古比宾!" 我认为编程环境中有不同的含义? (2认同)
  • 恐怕不会。它只是意味着“东西”、“事物”。 (2认同)

Ala*_*lan 5

我认为协方差在声明返回特定类而不是其基类的工厂方法时很有用。 本文很好地解释了这种情况,并包括以下代码示例:

class product
{
    ...
};

class factory
{
public:
    virtual product *create() const = 0;
    ...
};
Run Code Online (Sandbox Code Playgroud)
class concrete_product : public product
{
    ...
};

class concrete_factory : public factory
{
public:
    virtual concrete_product *create() const
    {
        return new concrete_product;
    }
    ...
};
Run Code Online (Sandbox Code Playgroud)