通常,协变允许您在派生类接口中表达比在基类接口中更多的信息。派生类的行为比基类的行为更具体,而协方差表达了差异(的一个方面)。
当您有相关的 gubbins 层次结构时,它很有用,在某些客户端想要使用基类接口但其他客户端将使用派生类接口的情况下。省略 const 正确性:
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();
};
知道他们有一个网页的客户(也许是浏览器)知道查看查询参数是有意义的。使用 Resource 基类的客户端不知道这样的事情。它们将始终将返回HttpAddress&的值绑定到URI&变量或临时变量。
如果他们怀疑但不知道他们的 Resource 对象有一个 HttpAddress,那么他们可以dynamic_cast. 但是协方差优于“只知道”并进行强制转换,原因与静态类型完全有用的原因相同。
还有其他选择 - 坚持使用该getQueryParam功能,URI但hasQueryParam对所有内容都返回 false(使 URI 界面混乱)。保留WebPage::getIdentifier定义为 return URL&,实际上返回一个HttpIdentifier&,并让调用者做一个毫无意义的事情dynamic_cast(混乱的调用代码,以及你说“返回的 URL 保证可动态转换为 HttpAddress”的 WebPage 的文档)。添加一个getHttpIdentifier功能WebPage(使WebPage界面混乱)。或者只是使用协方差来表示它的意思,即表示 aWebPage没有 anFtpAddress或 a MailtoAddress,它有一个HttpAddress.
最后当然有一个合理的论点,即您不应该有 gubbins 的层次结构,更不用说相关的 gubbins 层次结构了。但是这些类可以很容易地成为纯虚方法的接口,所以我认为它不会影响使用协方差的有效性。