从 void 到 std::any:现代 C++ 类型系统的进化之路

开发
想象一下,你是一位魔法师,口袋里有个神奇的百宝箱,不管是数字、字符串、还是你自己发明的各种稀奇古怪的类型,统统都能往里面塞!这不就是我们今天要认识的新朋友std::any 嘛。

嘿嘿!亲爱的码农小伙伴们 👋 ,今天咱们要一起探索一个超级炫酷的 C++17 新特性 -std::any 🎁!

想象一下,你是一位魔法师 🧙♂️,口袋里有个神奇的百宝箱 🎩,不管是数字 🔢、字符串 📝、还是你自己发明的各种稀奇古怪的类型 🎨,统统都能往里面塞!这不就是我们今天要认识的新朋友std::any 嘛~ 它就像是代码世界里的哆啦A梦口袋 🌈,百变又神奇!

为什么需要 std::any?

我们先来聊聊在 C++17 之前,我们是怎么处理"存储任意类型数据"这个让人头疼的问题的~

想象一下,你是个魔法师学徒 🧙♂️,需要一个百宝袋来存放各种神奇的物品。但是在 C++17 之前,我们只有一些不太完美的选择...

首先,我们试过用void* 指针这个调皮的小家伙 🎭:

void* data = nullptr;
data = new int(42);       // 往袋子里放个数字
data = new std::string(); // 再放个字符串

哎呀呀~这个方法就像是蒙着眼睛往袋子里塞东西 🙈,不仅容易摔跤(类型不安全),还得自己记得打扫卫生(手动管理内存)。多不优雅呀!

那要不试试联合体(union)?这个小机灵鬼看起来挺不错的 🎪:

union Data {
    int n;
    float f;
    char str[20];
};

但是等等!这个小家伙有点挑食呢 😅 —— 它只喜欢简单的类型,遇到std::string 这样的复杂类型就摇头说"不要不要"了~

那继承体系呢?让所有物品都继承自一个基类 🌳:

class Base { 
    virtual ~Base() = default; 
};
class Derived : public Base { /*...*/ };
std::vector<Base*> items;

嗯...这就像是要求所有的魔法物品都得先去魔法学校注册一样 📝,太麻烦啦!而且还得处理各种类型转换,就像给每个物品都贴上标签一样繁琐~

但是!现在我们有了std::any 这个超级百宝箱 ✨!它就像是哆啦A梦的四次元口袋一样神奇:

  • 想放什么类型就放什么类型 🎁
  • 不用担心内存泄漏 💫
  • 类型安全又可靠 🛡️
  • 使用起来简单又方便 🎯

让我们继续往下看,看看这个神奇的百宝箱要怎么用吧!🚀

基础用法

来来来,让我们一起召唤出这个超级神奇的魔法百宝箱吧!🪄✨ 就像打开哆啦A梦的四次元口袋一样令人期待呢~

#include <any>
#include <iostream>

int main() {
    std::any magicBox;    // 哒哒!变出一个百宝箱 🎁
    
    magicBox = 42;        // 哇!塞进去一个神秘数字 🔮
    magicBox = "魔法咒语"; // 嘿咻!变成了一串神秘咒语 ✨
    magicBox = 3.14159;   // 噗!又变成圆周率啦 🥧
    
    // 让我们念个咒语,看看能变出什么来~
    if (magicBox.has_value()) {  // 先偷偷看看盒子里有没有东西 👀
        // 先确认一下类型是否匹配 🔍
        if (magicBox.type() == typeid(double)) {
            double value = std::any_cast<double>(magicBox);
            std::cout << "变变变,出来吧!✨ " << value << std::endl;
        } else {
            std::cout << "咦?这不是我要的类型呢~ 🤔" << std::endl;
        }
    }
}

哇哦!看到了吗?这个神奇的百宝箱简直就像是会魔法一样呢!🎩 它就像是一个调皮的变色龙 🦎,可以随心所欲地变成任何类型。今天是个活泼的整数 🔢,明天可能就摇身一变成了优雅的字符串 📝,后天说不定又变成了神秘的浮点数 🎯 呢!

我们用type() 和typeid 来检查类型,就像是用魔法镜子 🪞 先看看盒子里装的是什么,确认无误后再打开,这样就不会有意外啦!而has_value() 就像是偷偷掀开盒子的一角,看看里面是不是真的藏着宝贝 👀。

是不是感觉编程也可以这么有趣呀?就像在魔法学校上课一样,每天都能学到新的咒语,创造出更多神奇的魔法!✨ 快来试试这个百变魔法盒吧~ 🎪🌈

实战示例:打造魔法世界的背包系统 🎮

嘿嘿,小伙伴们!既然我们已经掌握了std::any 这个神奇的魔法 ✨,不如来点更有趣的?让我们一起打造一个超级可爱的游戏背包系统吧!想象你是个小魔法师 🧙♂️,口袋里装着各种神奇的道具...

首先,我们需要一些有趣的道具类型,就像在魔法商店里挑选宝贝一样 🏪:

#include <any>
#include <string>
#include <vector>
#include <iostream>

// 先来几个有趣的道具类型 🎨
struct Weapon { 
    std::string name;
    int damage;
};

struct Potion {
    std::string color;
    int healing;
};

struct Coin {
    int value;
};

看看这些可爱的道具!我们有威力十足的武器 ⚔️、神奇的药水 🧪,还有闪闪发光的金币 💰!每个道具都有自己的特点,就像魔法世界里的宝贝一样~

接下来,让我们创造一个神奇的背包来装这些宝贝 🎒:

class MagicBag {
    std::vector<std::any> items;  // 这就是我们的魔法背包啦!🎒
public:
    // 往背包里塞东西,就像变魔术一样简单!✨
    void addItem(const std::any& item) {
        items.push_back(item);
        std::cout << "哇!背包里多了个神秘物品!" << std::endl;
    }
}

瞧瞧这个可爱的背包!它就像哆啦A梦的四次元口袋一样,什么都能装进去 🌈。每次添加物品的时候,背包都会开心地告诉我们:"哇!又有新宝贝啦!" 🎉

但是等等,我们怎么知道背包里装了什么呢?来看看这个神奇的探索功能 🔍:

    void inspectBag() {
        std::cout << "让我们看看魔法背包里有什么~ 🔍" << std::endl;
        for (const auto& item : items) {
            if (item.type() == typeid(Weapon)) {
                auto& weapon = std::any_cast<const Weapon&>(item);
                std::cout << "发现一把武器:" << weapon.name 
                         << ",伤害值:" << weapon.damage << " ⚔️" << std::endl;
            }
            // ... 其他类型的检查 ...
        }
    }
};

哇哦!这就像是在玩寻宝游戏一样呢 🗺️!每次查看背包,我们都能知道里面藏着什么宝贝。是威力强大的武器?还是神奇的药水?亦或是闪闪发光的金币?就让我们来试试这个神奇的背包吧 ✨:

int main() {
    MagicBag bag;  // 创建一个魔法背包 ✨
    
    // 往背包里放些宝贝吧!
    bag.addItem(Weapon{"火焰剑", 100});         // 放入一把超酷的武器 ⚔️
    bag.addItem(Potion{"蓝色", 50});           // 来点神奇药水 🧪
    bag.addItem(Coin{1000});                   // 当然要带上金币啦 💰
    
    bag.inspectBag();  // 让我们看看背包里都有什么呀~
    return 0;
}

是不是感觉特别有趣呀?🌟 通过这个可爱的背包系统,我们不仅学会了如何使用std::any,还体会到了编程的乐趣!就像在魔法世界里探险一样,每一行代码都充满了惊喜和期待 🎪

记住哦,虽然这只是个简单的例子,但它完美展示了std::any 的神奇之处。在实际开发中,我们可能需要更复杂的系统,但基本原理就是这么简单又有趣!现在,快拿起你的魔法棒,创造属于你的魔法背包吧!🪄✨

std::any 的高级特性

哎呀,小伙伴们!我们的魔法百宝箱std::any 还藏着好多有趣的小机关呢!🎁 让我们一起来探索这些神奇的魔法吧~

首先,我们来看看怎么确认百宝箱里是不是真的藏着宝贝 🔍:

std::any treasureBox;  // 创建一个神奇的百宝箱 ✨
std::cout << "百宝箱是空的吗?" << (!treasureBox.has_value() ? "是呀是呀~" : "里面有宝贝哦!") << std::endl;

treasureBox = 42;  // 往里面放个幸运数字 🎲
std::cout << "现在百宝箱里有宝贝啦!" << (treasureBox.has_value() ? "没错!" : "咦?怎么不见了?") << std::endl;

哇!has_value() 就像是个调皮的小精灵 🧚♀️,帮我们偷偷看看盒子里是不是藏着宝贝呢!

那要是想把宝贝拿出来该怎么办呢?来看看这个神奇的咒语 🪄:

treasureBox = "魔法水晶球";  // 放入一个水晶球 🔮

// 在取出宝贝前,先确认一下类型是否正确 🔍
if (treasureBox.type() == typeid(int)) {
    // 类型匹配,可以安全取出
    int value = std::any_cast<int>(treasureBox);
    std::cout << "成功取出数字:" << value << " ✨" << std::endl;
} else {
    std::cout << "哎呀呀~这不是我们要的类型呢!要用对咒语才行哦~ 🌟" << std::endl;
}

看到了吗?我们先用type() 检查类型是否匹配,这样就不会出现意外啦!这就像是在打开百宝箱前,先用魔法镜子看看里面装的是什么 🪞

还有一个超级厉害的技能,我们可以直接问问盒子里装的是什么类型的宝贝 📦:

treasureBox = 3.14159;  // 放入圆周率 🥧
std::cout << "让我猜猜盒子里是什么类型的宝贝~" << std::endl;
std::cout << "是不是浮点数呢?" << (treasureBox.type() == typeid(double) ? "猜对啦!🎉" : "猜错啦!😅") << std

type() 就像是个诚实的小助手 🤖,总是会告诉我们盒子里到底藏着什么类型的宝贝~

最后还有一个神奇的重置魔法,可以让盒子恢复到空空如也的状态 ✨:

treasureBox.reset();  // 施展重置魔法!
std::cout << "盒子被清空啦~" << (!treasureBox.has_value() ? "果然是空的!🌟" : "咦?还有东西?🤔") << std::endl;

reset() 就像是一个清理咒语 🧹,可以让魔法盒子重新焕然一新,准备装入新的宝贝!

是不是感觉std::any 越来越有趣了呢?它就像是一个会变魔术的小盒子 🎩,不仅能装各种各样的宝贝,还能告诉我们里面藏着什么。记得在使用的时候要先检查类型是否匹配,这样就不会遇到意外啦!✨

记住哦,魔法虽然强大,但也要小心使用~毕竟每次施法都需要消耗一些魔力值(性能开销)呢!🔮 下次遇到需要存储不同类型数据的场景,就可以想起这个神奇的百宝箱啦!🎁

使用注意事项

嘿!亲爱的魔法师朋友们 🧙♂️,在你开始疯狂使用这个神奇的std::any 百宝箱之前,让我们来聊聊几个使用魔法时需要注意的小秘密吧!🤫

你知道吗?每次使用std::any 的魔法都需要消耗一些魔力值(也就是运行时开销)哦!这里说的"魔力值"其实就是程序运行时的性能开销,主要包括:

  • 内存开销:std::any 需要在堆上动态分配内存来存储数据 💾
  • 类型擦除开销:它需要额外的机制来记住和管理存储的类型信息 📝
  • 类型检查开销:每次取值时都需要进行运行时的类型检查 🔍
  • 复制开销:在存储和提取值时可能产生额外的对象复制 📦

就像是在玩魔法游戏时要留意魔力条一样 🎮。所以呀,不要太贪心,不是所有东西都要往百宝箱里塞哦!来看个例子:

std::vector<std::any> inventory;  // 这是我们的百宝箱 🎁
// 不推荐这样频繁使用
for (int i = 0; i < 10000; i++) {
    inventory.push_back(std::any(i));  // 每次都消耗魔力值!😰
}

哎呀呀,这样会消耗好多魔力值呢!如果你知道只会存储数字,那还是乖乖用std::vector<int> 吧!🎯

还有啊,每次从百宝箱里取东西的时候,一定要像个细心的魔法师一样,先确认类型是否正确哦!不然可能会被std::bad_any_cast 这个调皮的小精灵捣乱呢!😝

std::any magicBox = "魔法药水";  // 放入一瓶药水 🧪
try {
    int number = std::any_cast<int>(magicBox);  // 糟糕!类型不对!🙈
} catch (const std::bad_any_cast& e) {
    std::cout << "哎呀~搞错啦!这不是数字而是药水呢!🤪" << std::endl;
}

诶,对了!如果你已经知道你的百宝箱里只会放特定的几样宝贝,比如只放武器⚔️、药水🧪和金币💰,那么使用std::variant 这个更专业的魔法容器可能会更好哦!它就像是一个定制版的百宝箱,更安全也更节省魔力值呢!

std::variant<Weapon, Potion, Coin> specialBox;  // 专门的百宝箱 ✨
specialBox = Weapon{"火焰剑", 100};  // 完美契合!🎯

记住啦,魔法虽然强大,但也要适可而止。就像吃糖果一样,吃太多可是会蛀牙的哦!🍬 选择合适的魔法工具,让你的代码既优雅又高效,这才是一个真正的代码魔法师呢!✨

让我们一起成为既会用魔法,又懂得节制的智慧魔法师吧!🎩✨

相关类型对比

嘿嘿,小伙伴们!既然我们已经认识了神奇的std::any 🎩,不如来认识认识它的亲戚们吧!就像魔法世界里的大家庭一样,每个成员都有自己的独特魔法呢~ ✨

首先来认识一下std::variant 这位帅气的表哥 🤴:

std::variant<int, std::string, double> magicBox;  // 这个盒子只能放特定的宝贝哦!
magicBox = 42;        // 放个数字进去 🔢
magicBox = "魔法咒语";  // 变成字符串啦 📝

看到了吗?std::variant 就像是个挑剔的魔法盒子,它可不是随随便便什么东西都往里塞的!它要提前说好能放什么类型,但正因为这样,它比std::any 更安全也更节省魔力值呢!🎯

接下来是可爱的表妹std::optional 🎀:

std::optional<std::string> maybeSpell;  // 可能有魔法咒语,也可能没有~
if (!maybeSpell.has_value()) {
    std::cout << "哎呀,魔法咒语不见啦!🙈" << std::endl;
}
maybeSpell = "阿拉霍洞开";  // 找到咒语啦!✨

这个小可爱特别适合处理那些"可能有,可能没有"的情况,就像薛定谔的猫一样神奇呢!🐱

最后是功能强大的堂哥std::function 💪:

std::function<int(int, int)> magicSpell;  // 这是一个神奇的魔法函数!
magicSpell = [](int a, int b) { return a + b; };  // 设置加法魔法 ➕
std::cout << "魔法计算:" << magicSpell(10, 20) << " ✨" << std::endl;

这位堂哥可厉害啦!他能把各种各样的函数、lambda 表达式都装进去,就像是个百变的魔法师傅!🧙♂️

记住哦,每个魔法工具都有它最擅长的领域:

  • 想要百变自由?找std::any 🎭
  • 需要类型安全?选std::variant 🛡️
  • 处理可能为空的值?用std::optional 🎁
  • 包装各种函数?用std::function 🎯

就像魔法世界里的法术一样,选对了工具,写代码也能变得充满乐趣呢!现在,快拿起你的魔法棒,去创造属于你的魔法程序吧!🪄✨

责任编辑:赵宁宁 来源: everystep
相关推荐

2024-10-14 15:04:15

C++std::any存储

2023-10-04 00:38:30

C++原子

2024-02-26 00:05:00

C++开发

2010-01-21 16:08:26

C++语言

2023-12-13 10:08:59

C++原子代码

2024-09-26 10:29:56

数据中台数据飞轮

2024-02-04 09:13:24

基础设施代码DevOps

2024-03-05 10:34:33

KubernetesPod云原生

2010-02-06 09:53:26

C++ void

2011-07-13 17:42:32

CC++

2011-07-13 17:08:02

CC++

2011-07-13 16:48:55

CC++

2023-01-04 11:04:32

2021-12-06 23:00:36

CC++编程语言

2015-06-25 11:21:33

C++Objective-C

2016-10-20 16:07:11

C++Modern C++异步

2024-12-26 08:00:38

2010-02-06 09:59:54

C++ void使用规

2022-11-01 12:16:47

Nginx微服务编译

2023-10-25 13:27:20

C++字符串
点赞
收藏

51CTO技术栈公众号