何为C++中的协变

C++中的协变

文章目录

前言

​ 在面向对象编程中,继承和多态是核心概念,它们允许程序员创建灵活的代码结构,可以轻松扩展和维护。C++中的协变是这些概念的一个高级应用,它允许派生类方法改变其返回类型,以返回更具体的派生类型。这种能力不仅增强了类型安全,还提供了更丰富的接口设计选项。


一、协变的概念

派生类重写基类虚函数时,与基类虚函数返回值类型不同 。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。

二、构成协变的条件及案例

案例1:

父类、子类返回各自的指针或引用

​ 协变最常见的用途是在实现一个对象的克隆操作时。例如,一个基类定义了一个返回基类指针的clone方法,而在派生类中,我们希望clone返回派生类的指针,这样就可以保留对象的完整类型信息。

首先给出一段比较标准的协变示例代码:

cpp 复制代码
// 父类、子类返回各自的指针或引用
class Base 
{
public:
	virtual Base* clone() const { return new Base(*this); }
};

class Derived : public Base 
{
public:
	virtual Derived* clone() const override { return new Derived(*this); }
};

测试用例代码:

cpp 复制代码
void test1()
{
	Base b;
	Base* p1 = b.clone();

	Derived d;
	Derived* p2 = d.clone();
}

在上面的代码中,Derived类重写了Base类的clone方法,并改变了返回类型为Derived*

案例2:

父类、子类分别返回其他父子类的指针或引用

cpp 复制代码
// 首先任意给出一个父类及其派生类
class A {};
class B : public A{};

// 接着定义另外一对父子类,其中构成多态的函数返回值类型分别为已有的父子类型
class Person 
{
public:
	virtual A* f() 
	{ 
		cout << "A::f()" << endl;
		return new A;
	}
};

class Student : public Person 
{
public:
	virtual B* f() 
	{
		cout << "B::f()" << endl;
		return new B;
	}
};

注意:上面代码中的Person类和Student类中的 f() 函数重写构成多态的前提下,函数返回值不同的情况对应只能是父类中的 f() 函数返回值为父类,子类同理。

案例3:

类似的,在设计模式中也存在协变的案例,比如工厂模式:

首先给出一个产品基类和产品实体类:

cpp 复制代码
// 产品基类
class Product {
public:
    virtual void use() = 0;
};

// 具体产品A
class ConcreteProductA : public Product {
public:
    void use() override {
        std::cout << "Using ConcreteProductA" << std::endl;
    }
};

// 具体产品B
class ConcreteProductB : public Product {
public:
    void use() override {
        std::cout << "Using ConcreteProductB" << std::endl;
    }
};

下面是对应工厂类的多态案例:

(注意子类重写的函数返回值都是父类指针,但是指向的是子类对象)

cpp 复制代码
// 工厂基类
class Factory {
public:
    virtual Product* createProduct() = 0;
};

// 具体工厂A
class ConcreteFactoryA : public Factory {
public:
    Product* createProduct() override {
        return new ConcreteProductA();
    }
};

// 具体工厂B
class ConcreteFactoryB : public Factory {
public:
    Product* createProduct() override {
        return new ConcreteProductB();
    }
};

类似的,下面是工厂类协变的案例:

(注意子类重写的函数返回值都是子类指针,父子类对应函数返回值类型不同)

cpp 复制代码
class Factory {
public:
    virtual Product* createProduct() = 0;
    virtual ~Factory() {}
};

class ConcreteFactoryA : public Factory {
public:
    ConcreteProductA* createProduct() override {
        return new ConcreteProductA();
    }
};

class ConcreteFactoryB : public Factory {
public:
    ConcreteProductB* createProduct() override {
        return new ConcreteProductB();
    }
};

总结

​ 通过本文的探讨,我们了解到协变是C++中一个强大而有用的特性,它允许派生类在保持多态行为的同时,返回更具体的类型。这不仅使得代码更加直观,也为设计模式的实现提供了更大的灵活性。然而,使用协变时需要谨慎,确保不违反基本的继承原则和类型安全。

相关推荐
微学AI13 分钟前
内网穿透的应用-把飞牛NAS变成家庭录像中心:EasyNVR部署与远程回放实战
开发语言·php
峥嵘life19 分钟前
Android WiFi连接过程 wpa_supplicant 日志分析
android·开发语言·php
BerryS3N24 分钟前
深度解析:从零构建生产级大模型 RAG(检索增强生成)系统全栈指南
开发语言·python·ai
今儿敲了吗39 分钟前
Python——函数基础
开发语言·笔记·python
CHANG_THE_WORLD1 小时前
深入理解递归:从函数调用到调用栈的完整执行过程
java·开发语言·算法
kels88991 小时前
单连接动态增减订阅:股票行情API后端降负载实战方案
开发语言·笔记·python·信息可视化·金融
一拳一个呆瓜1 小时前
【STL】iostream 编程:使用提取运算符
c++·stl
a4493153622 小时前
MacBook USB-C不充电故障诊断:从PD握手到主板供电链路
c语言·开发语言·macos·电脑
德福危险2 小时前
从目录枚举到bash破壳漏洞、从流量抓包到组权限提权:靶机练习之symfonos3
开发语言·bash
meng半颗糖2 小时前
3.Java流程控制语句
java·开发语言·intellij-idea