C++中Covariant返回值类型详解

目录
  • 前言
  • 什么是协变返回值类型(Covariant)
  • 协变返回值类型(Covariant)的作用

前言

C++中当子类覆写(override)父类虚函数时,子类函数的返回值类型可以和父类函数的返回值类型不一致吗?
先说结论:可以,但当且仅当它们的返回值类型是协变返回值类型(Covariant)时可以。C++中gcc从3.4开始支持这一特性。

什么是协变返回值类型(Covariant)

函数的协变返回值类型指的是子类中的成员函数的返回值类型不必严格等同与该函数所重写的父类中的函数的返回值类型,而可以是更 “狭窄” 的类型。C++/Java等面向对象编程语言均支持协变返回值类型。

例子:

class Shape {
public:
  virtual ~Shape() { }
  virtual Shape* clone()  const = 0;   // Uses the copy constructor
  virtual Shape* create() const = 0;   // Uses the default constructor
};
class Circle : public Shape {
public:
  Circle* clone()  const;   // Covariant Return Types; see below
  Circle* create() const;   // Covariant Return Types; see below

};
Circle* Circle::clone()  const { return new Circle(*this); }
Circle* Circle::create() const { return new Circle();      }

C++中不支持virtual constructor,因为:

  • 创建对象时需要知道对象的完整信息
  • 虚函数机制也决定了对象尚未创建时,类的virtual table或许还不存在
  • 我们不可能有指向virtual constructor的指针

但是我们可以通过上面的代码实现类似的想法,如果我们拥有指向对象的指针:

  • 通过clone()调用对象的拷贝构造函数,复制当前的对象
  • 通过create()调用默认构造函数,创建新的对象

比如下面的使用场景:

void userCode(Shape* s)
{
  Shape* s2 = s->clone();
  Shape* s3 = s->create();
  // ...
  delete s2;
  delete s3;
}

如果指针指向的是基类对象,调用上述函数时返回的就是指向基类对象的指针并赋值给s2/s3,如果指针指向的是子类对象,调用上述函数时返回的就是指向子类对象的指针并赋值给s2/s3。

协变返回值类型(Covariant)的作用

协变返回类型到底有什么用呢,编译器为什么要支持这种语法?如果编译器不支持,上面的例子将只能写成如下这样:

class Shape {
public:
  virtual ~Shape() { }
  virtual Shape* clone()  const = 0;   // Uses the copy constructor
  virtual Shape* create() const = 0;   // Uses the default constructor
};
class Circle : public Shape {
public:
  Shape* clone()  const;   // Covariant Return Types; see below
  Shape* create() const;   // Covariant Return Types; see below

};
Shape* Circle::clone()  const { return new Circle(*this); }
Shape* Circle::create() const { return new Circle();      }

这样上面的userCode函数将不能通过编译,上面调用clone函数部分将不得不改写成下面这样:

 void userCode(Shape* s)
{
  Shape* s2 = s->clone();
  Circle* c = dynamic_cast<Circle*>(s2);
  if (c != NULL) {
     // c point to Circle
  } else {
	  if (s2 != NULL) {
	     // s2 point to base Shape
	     }
	  }
  }
  // ...
  delete s2;
}

通过if/else分支来判断s是指向子类Circle还是指向基类Shape,失去了动态绑定的意义。

到此这篇关于C++中Covariant返回值类型详解的文章就介绍到这了,更多相关C++ Covariant内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++11获取线程返回值的实现代码

    C++11 std::future and std::promise 在许多时候,我们会有这样的需求--即我们想要得到线程返回的值. 但是在C++11 多线程中我们注意到,std::thread对象会忽略顶层函数的返回值. 那问题来了,我们要怎么获得线程的返回值呢? 我们通过一个例子来说明如何实现这个需求. 假设我们的app会创建一个线程来压缩一个文件夹,该线程在压缩完文件夹后会返回压缩文件 *.zip 和这个zip文件的大小,我们现在就想获得这个线程的返回值. 有两种方法可以实现这个需求: 1

  • 详解C++ 运算符重载中返回值的坑

    相信不少朋友在学习运算符重载的时候,都会被参数与返回值应该是左值引用,还是右值引用,还是const常量所困扰.当然我无法一一枚举,这次先讲一下返回值的坑 (没错就是我亲手写的bug) E0334 "Myclass" 没有适当的复制构造函数 其实这个问题的根源是,没有定义常量参数类型的拷贝构造函数所致 先来看看代码 //头文件head.h class Myclass { private: int a; public: Myclass(int b=0):a(b) {} //构造函数 Myc

  • C++11特性小结之decltype、类内初始化、列表初始化返回值

    作用:返回表达式或变量的类型 返回值规则: 若e是一个左值(lvalue,即"可寻址值"),则decltype(e)将返回T& 若e是一个临终值(xvalue),则返回值为T&& 若e是一个纯右值(prvalue),则返回值为T decltype()不会执行括号内的表达式,decltype返回的类型是用于声明的,不能用于单纯的判断.比如decltype(a)==int,是不可以的,只能是在定义新的变量.返回值的地方使用: int a=1; decltype(a)

  • C++多线程获取返回值方法详解

    在许多时候,我们会有这样的需求--即我们想要得到线程返回的值.但是在C++11 多线程中我们注意到,std::thread对象会忽略顶层函数的返回值. 那问题来了,我们要怎么获得线程的返回值呢? 我们通过一个例子来说明如何实现这个需求.用多个线程计算(a+b)/ (x+y) 的值 有两种方法,分别是 1. 传统的方法:在线程间共享指针 #include<iostream> #include<thread> #include<mutex> #include<atom

  • 如何让C++函数返回值死心塌地为你工作

    本文主要研究的是C++函数返回值,你必须注意的问题,下面进入正题. C++太繁杂了,先接触C++后接触python这样的语言,你就再也不想碰它,因为,就连一个函数返回值都一大堆的说道,这里面的玄机,连工作三年的C++熟手都未必能准确的理解和运用. 归根结底,C++所面临的问题要求它提供各种各样的机制以保证性能,也许,这辈子也见不到C++能安全有效的自己进行内存垃圾回收..... 老程序猿都会提醒菜鸟,注意函数的返回值,因为,很可能,你的函数返回的数据在后续的使用中会出错.那么函数在返回值时要注意

  • C++返回值类型后置实现(跟踪返回值类型)

    在泛型编程中,可能需要通过参数的运算来得到返回值的类型.考虑下面这个场景: template <typename R, typename T, typename U> R add(T t, U u) { return t+u; } int a = 1; float b = 2.0; auto c = add<decltype(a + b)>(a, b); 我们并不关心 a+b 的类型是什么,因此,只需要通过 decltype(a+b) 直接得到返回值类型即可.但是像上面这样使用十分

  • C++中Covariant返回值类型详解

    目录 前言 什么是协变返回值类型(Covariant) 协变返回值类型(Covariant)的作用 前言 C++中当子类覆写(override)父类虚函数时,子类函数的返回值类型可以和父类函数的返回值类型不一致吗?先说结论:可以,但当且仅当它们的返回值类型是协变返回值类型(Covariant)时可以.C++中gcc从3.4开始支持这一特性. 什么是协变返回值类型(Covariant) 函数的协变返回值类型指的是子类中的成员函数的返回值类型不必严格等同与该函数所重写的父类中的函数的返回值类型,而可

  • C# WebApi 接口返回值不困惑:返回值类型详解

    前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi的园友们速速动起来,跟着博主一起来学习吧.之前分享过一篇C#进阶系列--WebApi接口传参不再困惑:传参详解,这篇博文内容本身很基础,没想到引起很多园友关注,感谢大家的支持.作为程序猿,我们都知道参数和返回值是编程领域不可分割的两大块,此前分享了下WebApi的传参机制,今天再来看看WebApi里面另一个重要而又基础的知识点:返回值.还是那句话:本篇针对初初使用WebApi的同学们,比较基础,有兴趣的且看看.

  • Django中get()和filter()返回值区别详解

    先上官方文档! filter(**kwargs) 返回包含与给定查找参数匹配的对象的新查询集. 简单来说,返回一个又对象组成的查询集合 get(**kwargs) 返回与给定查找参数匹配的对象,该对象应采用字段查找中描述的格式. 例子 例如在Model中有一个Order类,包含一个id字段,输入 id 为2019 字段的 id 1.get()方法 orders = Orders.objects.get(id=20190003) print(order) 先查看orders是什么,结果为 Orde

  • Java泛型映射不同的值类型详解及实例代码

    Java泛型映射不同的值类型详解 前言: 一般来说,开发人员偶尔会遇到这样的情形: 在一个特定容器中映射任意类型的值.然而Java 集合API只提供了参数化的容器.这限制了类型安全地使用HashMap,如单一的值类型.但如果想混合苹果和梨,该怎样做呢? 幸运的是,有一个简单的设计模式允许使用Java泛型映射不同的值类型,Joshua Bloch在其<Effective Java>(第二版,第29项)中将其描述为类型安全的异构容器(typesafe hetereogeneous Containe

  • Python中函数的参数类型详解

    目录 1.Python的函数参数的类型 2.Python的必传参数 3.关键字参数 4.默认参数 5.不定长参数 1.Python的函数参数的类型 必传参数:调用函数时必须传入的参数,函数定义时只定义参数名 关键字参数:传入时以函数的参数名值对的方式传入 默认参数:函数定义时为参数默认设置一个值,调用时不传输参数即以默认值处理 不定长参数:以*修饰或者**修饰的参数;*修饰的参数是一个元组(tuple),**修饰的参数必须是字典(dict),通常写作*args或者**args 2.Python的

  • PHP方法的返回值示例详解

    前言 不仅是PHP,大部分编程语言的函数或者叫方法,都可以用return来定义方法的返回值.从函数这个叫法来看,本身它就是一个计算操作,因此,计算总会有个结果,如果你在方法体中处理了结果,比如进行了持久化保存,那么这个函数就不用返回任何内容.而计算的结果是要给外部使用的,这时候就要将计算结果进行返回了. return关键字 function testA($a, $b) { echo $a + $b; } var_dump(testA(1, 2)); // NULL function testB(

  • Shell中关于处理方法返回值问题详解

    背景 如今,不会Linux的程序员都不意思说自己是程序员,而不会shell编程就不能说自己会Linux.通过shell编程,写一些工具批处理的时候,经常需要自定义函数.更复杂点的情况下,可能有需要返回一个值. 由于在shell的世界中,并不像其他编程语言,它不支持我们所熟悉的方法返回.本文一起总结一下如何优雅的解决返回值问题? 测试程序 我们一般通过$?来获取上一个语句的输出.看一下下面得测试语句: 新建testReturn脚本 returnString(){ return $1 } retur

  • Selenium执行Javascript脚本参数及返回值过程详解

    在Selenium中可以使用drvier.execute_script()来执行Javascript脚本,支持多行语句. 使用Javascript可以实现以下功能: 移除元素隐藏.禁用.只读等限制属性 为元素添加id或高亮样式 页面滚动 富文本框输入(HTML注入) 获取页面信息 使用Javascript参数 在使用Javascript语句时,还可以动态传入参数或元素对象,Javascript语句中使用占位符"argument[n]"来表示取第几个参数,如: js = "ar

  • MyBatis中传入参数parameterType类型详解

    前言 Mybatis的Mapper文件中的select.insert.update.delete元素中有一个parameterType属性,用于对应的mapper接口方法接受的参数类型.本文主要给大家介绍了关于MyBatis传入参数parameterType类型的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1. MyBatis的传入参数parameterType类型分两种 1. 1. 基本数据类型:int,string,long,Date; 1. 2. 复杂数据类

随机推荐