写点什么

基于 std::variant 的运行时多态

作者:SkyFire
  • 2023-01-28
    陕西
  • 本文字数:2197 字

    阅读完需:约 7 分钟

基于 std::variant 的运行时多态

C++ 的运行时多态实现方式主要是虚函数,而基于虚函数的多态相信但凡学过一些 C++的人都了解,所以本文不打算介绍基于虚函数实现的多态,而是介绍一种基于 C++20 新特性的多态实现方式。

多态的含义

在 C++ 中,多态是指一个函数或对象可以在运行时根据调用它的对象类型进行不同的行为。这是通过使用基类指针或引用调用派生类对象来实现的。


对于函数,多态意味着一个基类的函数可以在其派生类中重定义,并在调用时使用相应的版本。这称为虚函数,可以使用 virtual 关键字声明。


对于对象,多态意味着可以使用基类指针或引用访问派生类对象,并在运行时调用正确的版本。这称为对象多态。


多态允许在不知道对象的具体类型的情况下进行编程,这样可以更容易地扩展和维护代码,并在需要时添加新的派生类。


例如:


class Shape {public:    virtual void draw() = 0; // Pure virtual function};
class Rectangle : public Shape {public: void draw() { std::cout << "draw Rectangle" << std::endl; }};
class Circle : public Shape {public: void draw() { std::cout << "draw Circle" << std::endl; }};
int main() { Shape *s1 = new Rectangle; Shape *s2 = new Circle; s1->draw(); // Draw a rectangle s2->draw(); // Draw a circle return 0;}
复制代码


在这个例子中,Shape 类是一个基类,Rectangle 和 Circle 是它的派生类。由于 draw() 函数是虚函数,所以在运行时会调用相应的派生类中的版本。

多态的优点

多态有许多优秀的优点,主要有以下几点:


可扩展性: 多态使程序变得更加灵活和可扩展,可以很容易地添加新的派生类,而无需修改基类或使用基类的代码。


可重用性: 多态使得代码可以重用,因为可以使用基类的指针或引用访问派生类对象。


更好的抽象性: 多态使得程序变得更加抽象,因为可以在不知道对象的具体类型的情况下进行编程。


程序的可维护性: 多态使得程序变得更加可维护,因为可以很容易地更改或更新派生类而不影响基类或其它派生类。


灵活性: 多态使得程序变得更加灵活,因为可以在运行时根据对象的类型进行不同的行为。


总之,多态是面向对象编程中非常重要的概念,具有高度的抽象性和可扩展性,是实现程序灵活性和可重用性的有力工具。

Modern C++ 基于 variant 和 visit 的多态

std::variant 介绍

std::variant 是 C++17 标准库中的一种类型。它是一种可以存储不同类型的值的类型,类似于其它语言中的“可变类型”或“联合类型”。


使用 std::variant 需要指定可能存储的类型,如下所示:


std::variant<int, std::string, double> var;
复制代码


这个 variant 变量可以存储 int,string,double 类型的值


可以使用 std::get 函数来获取当前存储的值,如下所示:


std::get<int>(var);std::get<std::string>(var);std::get<double>(var);
复制代码


也可以使用 std::holds_alternative 函数来判断当前存储的值是否是指定类型,如下所示:


std::holds_alternative<int>(var);std::holds_alternative<std::string>(var);std::holds_alternative<double>(var);
复制代码


使用 visitor 来操作 variant 变量:


std::visit([](auto&& arg) {        // do something with arg            }, var);
复制代码


std::variant 是一种安全的类型,在存储和获取值时会进行类型检查,确保存储的值的类型与指定的类型匹配。


std::variant 是一种非常灵活的类型,可以用来实现各种各样的需求,如错误处理、解析和序列化等。

使用 std::variant 实现多态

使用 std::variant 重新实现上面基于虚函数实现的多态,代码如下:


#include <iostream>#include <variant>
class Rectangle {public: void draw() { std::cout << "draw Rectangle" << std::endl; }};
class Circle {public: void draw() { std::cout << "draw Circle" << std::endl; }};
using shape = std::variant<Rectangle, Circle>;void drawShape(auto& v) { std::visit([](auto& s){ s.draw(); }, v);}
int main() { shape s1 = Rectangle(); shape s2 = Circle();
drawShape(s1); drawShape(s2);}
复制代码


从上面的代码中可以看出,使用 std::variant 和 std::visit 实现的多态,类之间没有继承关系,更加灵活。


这种多态实现具有 subtype 运行时选择和 ad-hoc 类型解耦的双重特色。

subtype 和 ad-hoc 多态

subtype 多态

Subtype 基于继承和接口来实现。特点如下:


  1. 基于继承和接口: Subtype 多态基于继承和接口,通过继承和实现接口来实现。

  2. 虚函数实现: Subtype 多态通过虚函数实现,可以在运行时选择不同的实现。


总的来说 Subtype 多态是一种灵活、高效的编程技巧, 他能够在运行时选择实现, 并且能够通过继承和接口来组织代码,提高代码的重用性和可维护性。

ad-hoc 多态

Ad-hoc 允许在编译时选择不同的实现。特点如下:


  1. 没有继承或接口: Ad-hoc 多态不需要继承或接口,而是通过函数重载和重载运算符来实现。

  2. 通过函数重载实现: Ad-hoc 多态通过函数重载实现,可以在编译时选择不同的实现。


总的来说 Ad-hoc 多态是一种灵活、高效的编程技巧, 但是它没有继承和接口的优点, 也不能像虚函数一样在运行时选择实现。

对比

对于 subtype 来说,扩展一个类很容易,只需要从基类继承就好了,但是如果需要给类型增加一个行为就比较困难了。


而 ad-hoc 刚好相反,为所有类型增加一个方法很简单,但是想要增加一个类型就比较困难,因为会牵扯到已有的自由函数修改。

发布于: 刚刚阅读数: 4
用户头像

SkyFire

关注

这个cpper很懒,什么都没留下 2018-10-13 加入

会一点点cpp的苦逼码农

评论

发布
暂无评论
基于 std::variant 的运行时多态_c++_SkyFire_InfoQ写作社区