虚函数表是如何在你不知情的情况下工作的?

开发
C++既给了我们强大的工具来解决复杂问题,又帮我们处理了所有繁琐的底层细节。就像是有一个贴心的助手,帮我们打理好一切!

"又是一个阳光明媚的下午," 老张端着他那杯冒着热气的咖啡,悠闲地靠在办公椅上。"今天我们来聊点有趣的 - C++ 虚函数背后的故事。"

小王正在为项目中遇到的一个多态问题发愁,一听这话立马来了精神:"老张,我正愁着呢!虚函数表到底是怎么建立的?编译器在背后做了什么魔法?"

"哈哈," 老张笑着放下咖啡杯,眼睛里闪着智慧的光芒。"说起这个,还真是个有意思的话题。你知道吗,编译器在处理虚函数时,就像一个细心的建筑师,需要把每个类的'蓝图'都完美地规划好。"

虚函数表的创建过程

"说到虚函数表的创建过程," 老张悠闲地啜了一口香浓的咖啡 ☕️, 眼睛里闪烁着智慧的光芒 ✨, "这简直就像是编译器在玩一场精妙的积木游戏呢!"

小王托着下巴,一脸求知欲满满的样子 🤓:"哦?这游戏怎么玩的呀?"

"想象一下," 老张神秘地笑着说 🎭, "编译器就像一位魔法师 🧙♂️, 它挥动魔杖,先是为基类 Animal 变出一张神奇的表格,就像给大楼打地基一样 🏗️。这张表格里记录着所有虚函数的位置,就像一张藏宝图!"

"然后呢?" 小王被勾起了兴趣 👀。

"然后啊," 老张站起身来,手舞足蹈地比划着 🕺, "当派生类 Cat 出现时,编译器就像个细心的建筑师,先把基类的图纸完整复制过来,该改的改,该加的加。覆写的函数就像翻新装修 🏠,新增的虚函数就像在楼顶加盖新楼层 ⬆️!"

"所以说," 小王恍然大悟 💡, "每个类的虚函数表就像是一栋独特的大楼,地基格局都一样,但装修和加建可以不同?"

"没错!" 老张开心地打了个响指 👆, "而且最妙的是,这些'建筑工作'都是在编译的时候完成的 🏗️。等程序跑起来的时候,就只需要给对象一把钥匙(vptr)🔑,它就能找到属于自己的那栋楼啦!"

小王眼睛亮晶晶的 ✨:"原来如此!编译器真是太聪明了!"

"是啊," 老张得意地摸了摸下巴 🧔, "这就是为什么 C++ 虽然看起来复杂,但跑起来却超级快 🚀。因为所有的'施工工作'都提前做好啦!"

// 🦁 基类 Animal 定义
// 所有动物的基础类
class Animal {
public:
    // 虚析构函数确保正确释放内存 🗑️
    virtual ~Animal() {} 
    
    // 纯虚函数,所有动物都必须实现发声 🔊
    virtual void makeSound() = 0;
    
    // 基础的吃东西行为 🍽️
    virtual void eat() { } 
    
protected:
    std::string name;  // 动物的名字 📝
};

// 🐱 派生类 Cat 定义
// 继承自 Animal 的猫咪类
class Cat :public Animal {
public:
    // 猫咪的析构函数 🗑️
    ~Cat() override {} 
    
    // 猫咪特有的叫声实现 😺
    void makeSound() override {
        std::cout << "喵喵喵~" << std::endl;
    }
    
    // 猫咪的进食行为 🐟
    void eat() override {
        std::cout << "吃小鱼干" << std::endl;
    }
    
    // 猫咪特有的呼噜功能 😴
    virtual void purr() {
        std::cout << "呼噜呼噜" << std::endl;
    }
};

"让我们来看看编译器这位魔法师 🧙♂️ 是如何玩转虚函数表的~" 老张神秘兮兮地说道,眼睛里闪烁着智慧的光芒 ✨。

"想象编译器就像一位超级厉害的积木大师 🏗️,它先是为基类 Animal 搭建了一个完美的积木基座,上面整整齐齐地摆放着各种虚函数的指针,就像是一个个等待被召唤的小精灵 🧚♀️。每个小精灵都有自己的固定位置,析构函数站在最前面当门神 🚪,后面是各种虚函数排排坐 👥。"

"哇!那派生类呢?" 小王迫不及待地问道 🤓。

老张喝了口咖啡,继续说:"这就更有意思啦!当派生类 Cat 来报到时,编译器就像个认真的复制大师 📝,先把基类的积木布局原封不动地复制过来。然后呢,它就开始了魔法改造 ✨ —— 覆写的函数就像是替换掉旧积木,新增的虚函数就像是在顶部堆叠新的积木块 🎯。最后,它还会给每个 Cat 对象发一把神奇的钥匙 🔑(也就是 vptr),让它能找到属于自己的积木城堡~"

"整个过程就像变魔术一样神奇," 老张打了个响指 👆,"但其实所有的'魔法'都是在编译时就完成了。等程序跑起来的时候,所有的积木都已经摆好啦,对象们只需要拿着自己的钥匙去找对应的城堡就好啦!" 🏰

小王恍然大悟:"所以说,编译器就是在玩一个超级智能的积木游戏,而且还要确保每个类的积木都能完美匹配?" 🎮

"没错!" 老张开心地说,"而且最厉害的是,这个积木游戏玩得既优雅又高效 🚀。就像是提前帮你把所有的乐高都拼好了,运行时只需要看看说明书就知道每个功能在哪里,简直不要太方便!"

1. 基类虚函数表的创建 📝

// 瞧瞧这个神奇的魔法书结构 ✨
struct Animal_VTable {
    typedef void (*FuncPtr)();  // 每个函数就像一个魔法咒语 🪄
    FuncPtr entries[3] = {
        (FuncPtr)&Animal::~Animal,  // 守门员析构函数 🚪
        (FuncPtr)nullptr,           // 等待实现的纯虚函数 📝
        (FuncPtr)&Animal::eat       // 可以被覆盖的普通虚函数 🍽️
    };
};

"这就像是在玩一个超级智能的积木游戏 🎮!" 老张兴致勃勃地说,"每个函数指针就像一块特殊的积木,它们整整齐齐地排列在虚函数表中,随时准备响应召唤。而最神奇的是,这些魔法在编译时就全部施展完成了,运行时只需要轻轻一点 👆,就能找到正确的函数啦!"

小王听得入迷了:"所以说,编译器就是在帮我们提前搭建好了这座魔法城堡 🏰?"

"没错!" 老张开心地说,"而且这座城堡还会成为所有派生类的蓝图,让它们能在这个基础上建造出自己独特的宫殿 ✨。这就是 C++ 虚函数表的魔法,简单又优雅,是不是很有趣呀?" 🎯

2.派生类虚函数表的初始化 🔄

"让我们一起来看看派生类的虚函数表是如何诞生的吧!" 老张眨眨眼睛说道 ✨。"这就像是在玩一个超级有趣的积木游戏 🎮!"

"想象一下,当编译器遇到我们的 Cat 类时,它就像一位充满创意的建筑师 👷♂️,手里拿着基类 Animal 的蓝图。它先是把这份蓝图完完整整地复制了一份 📋,就像在玩'复制粘贴'的魔法游戏 ✨。"

struct Cat_VTable {
    typedef void (*FuncPtr)();
    FuncPtr entries[4];  // 这就像是一个神奇的魔法口袋 🎩
    
    Cat_VTable() {
        // 开始施展魔法 ✨
        entries[0] = (FuncPtr)&Cat::~Cat;        // 第一个魔法:猫咪的告别仪式 👋
        entries[1] = (FuncPtr)&Cat::makeSound;   // 第二个魔法:教会猫咪喵喵叫 😺
        entries[2] = (FuncPtr)&Cat::eat;         // 第三个魔法:让猫咪会吃小鱼干 🐟
        entries[3] = (FuncPtr)&Cat::purr;        // 第四个魔法:独特的呼噜技能 😴
    }
};

"瞧瞧这个神奇的表格!" 老张兴致勃勃地说,"它就像是一本魔法食谱 📖,每个函数指针都是一道独特的配方 🧪。基类定义的函数就像是必修课 📚,而新加的 purr 函数则是猫咪的特色选修课 🎓。最妙的是,这些魔法配方都是在编译时就准备好的,运行时只需要挥一挥魔杖(通过 vptr)就能立刻找到正确的咒语啦!" 🪄

"所以说," 小王恍然大悟,眼睛闪闪发亮 ✨,"每个猫咪对象都带着这本魔法书的钥匙,需要施展魔法时就能立刻翻到正确的页面?"

"完全正确!" 老张开心地打了个响指 👆,"这就是 C++ 虚函数表的魔法精髓,简单又优雅,是不是特别有趣呀?" 🎯

3.虚函数表的放置 📍

"说到虚函数表的放置啊," 老张神秘兮兮地压低声音说 🤫, "这可是编译器最爱显摆魔法的时刻!想象一下,编译器就像一位神奇的魔术师 🎩,它会把虚函数表这个宝贝疙瘩小心翼翼地放在程序的只读数据段里,就像把一颗珍贵的钻石放进保险箱 💎。"

// 瞧瞧这个神奇的魔法配方 ✨
static const Cat_VTable cat_vtable;  // 这就是我们的魔法宝典!📚

"但是等等,故事还没完呢!" 老张眨眨眼睛继续说 👀, "当一只可爱的小猫咪诞生的时候,编译器就会像给宝宝戴上魔法项链一样 🔮,给它一个特殊的 vptr 指针。这个指针就像是通向魔法世界的钥匙 🗝️,让小猫咪随时都能找到属于自己的那本魔法书!"

Cat::Cat() {
    vptr = &cat_vtable;  // 给小猫咪戴上魔法项链 ✨
    // ... 其他的猫咪打扮工作 ... 🎀
}

"你知道最神奇的是什么吗?" 老张神秘地笑着说 😏, "整个班级的小猫咪们都共用同一本魔法书,但每只猫咪都有自己的钥匙 🔑!这样不仅节省了内存空间 💾,还让所有的魔法咒语都能快速施展,简直是太聪明了!"

小王听得入迷了:"哇!所以说每个对象都带着自己的 vptr 钥匙 🗝️,但其实大家都在读同一本存在只读区的魔法书?这设计也太巧妙了吧!"

"没错!" 老张开心地说 😊, "这就是 C++ 的智慧啊 - 既保证了每个对象能快速找到自己的虚函数,又不会浪费内存空间。就像一个超级智能的图书管理系统,所有的魔法都触手可及!" 🌟

"注意这个过程中的几个关键点:" 老张强调道 ✨

  • "派生类会继承基类虚函数表的完整布局,保证函数位置的一致性 📋"
  • "覆写的函数直接替换对应位置的函数指针 🔄"
  • "新增的虚函数添加到表的末尾 ⬇️"
  • "编译器会自动处理所有的偏移量计算 🔢"

"这就像建造一栋大楼," 老张打了个比方 🏢, "基类定下基础布局,派生类可以装修改造,但主体结构必须保持一致,只能往上加新的楼层!"

"那如果是多重继承呢?" 小王问道 🤔

"啊,多重继承就更有意思了!" 老张眼睛一亮 ✨,"每个基类都会贡献自己的虚函数表,这就像..."

"等等!" 小王赶紧打断 🤚,"这个话题是不是得留到下次再聊?"

"哈哈,说得对!" 老张笑着说 😄,"多重继承的虚函数表确实是另一个精彩的故事了..."

小王若有所思地点点头 🤔:"所以说,虚函数表的创建是编译器在编译时就完成的工作,运行时只需要设置正确的 vptr 就可以了?"

"完全正确!" 老张赞许地说 👍,"这也是为什么虚函数的调用虽然有一次间接跳转,但整体性能还是很好的原因 - 因为所有的准备工作都在编译时完成了!"

"C++ 的设计真是既优雅又高效啊!" 小王感叹道 🌟

"是啊," 老张笑着说 😊, "这就是为什么即使过了这么多年,C++ 依然是性能敏感场景的首选语言之一。"

多重继承时的虚函数表是什么样的? 🌳

"你知道吗?" 老张眨眨眼睛说道 👀,"多重继承就像是在玩一个超级豪华的积木游戏 🎮!想象一下,我们的小鸭子 Duck 🦆 不仅要继承会飞的本领,还要继承会游泳的技能,这就像是要把两栋不同风格的大楼合并成一座超级大厦 🏢!"

"编译器这个小机灵鬼 🧚♂️ 会给每个基类都安排一个专属的虚函数表,就像是在大厦里设置多个前台接待处 🏨。每个前台都有自己的服务清单,但最终都是为同一位客人 - 也就是我们的 Duck 对象服务。"

"最神奇的是," 老张喝了口咖啡继续说 ☕️,"当我们用 Flying 指针指向鸭子时,编译器就会带我们走前门 🚪;用 Swimming 指针指向鸭子时,它就会带我们绕到侧门 🚶♂️。但不管从哪个门进去,最终都能找到我们要的服务!"

"而且你猜怎么着?" 老张神秘地压低声音 🤫,"Duck 自己独特的 quack 函数会被安排在第二个虚函数表的末尾,就像是在大厦顶层开了一间特色餐厅 🍽️ - 只有真正的鸭子才能找到这里!"

小王听得入迷了:"哇!这简直就像是在经营一家五星级酒店嘛!" 🏨

"没错!" 老张开心地说 😊,"但是要记住,这种豪华配置也是要付出代价的 - 每个虚函数表都需要一个指针,就像是要多养几个门卫一样 💂♂️,会让我们的对象变得稍微胖一点。不过只要用得其所,这点投资还是很值得的!"

"这就是 C++ 的魅力啊!" 老张总结道 🌟,"它让我们能够构建出如此精妙的设计,就像是在搭建一座充满魔法的城堡 🏰!"

小王来了兴趣:"哦?这听起来很复杂啊!" 🤔

"来来来," 老张神秘兮兮地说,一边拿起马克笔在白板上画起了示意图 ✏️,"让我给你变个魔术,看看多重继承是怎么玩的~"

class Flying {
public:
    virtual ~Flying() {}
    
    // 展翅高飞 🦅
    virtual void takeOff() { 
        std::cout << "起飞!" << std::endl; 
    }
    
    // 优雅降落 🛬
    virtual void land() { 
        std::cout << "着陆!" << std::endl; 
    }
};

class Swimming {
public:
    virtual ~Swimming() {}
    
    // 深潜探索 🏊
    virtual void dive() { 
        std::cout << "潜水!" << std::endl; 
    }
    
    // 轻轻漂浮 🌊
    virtual void float() { 
        std::cout << "漂浮!" << std::endl; 
    }
};

// 看我们的超级英雄鸭子闪亮登场 ✨
class Duck :public Flying, public Swimming {
public:
    ~Duck() override {}
    
    // 鸭子的专属绝技 🦆
    void takeOff() override { 
        std::cout << "鸭子起飞!" << std::endl; 
    }
    
    void land() override { 
        std::cout << "鸭子着陆!" << std::endl; 
    }
    
    void dive() override { 
        std::cout << "鸭子潜水!" << std::endl; 
    }
    
    void float() override { 
        std::cout << "鸭子漂浮!" << std::endl; 
    }
    
    // 独特的鸭子叫声 🔊
    virtual void quack() { 
        std::cout << "嘎嘎!" << std::endl; 
    }
};

"瞧瞧这个神奇的设计!" 老张眨眨眼睛说 👀,"我们的鸭子就像个全能选手,既能在天上翱翔 🦅,又能在水里遨游 🏊♂️。而编译器呢,就像个超级管家 🫅,它会给每个基类都安排一个专属的虚函数表,就像是给超级英雄准备了不同的装备间一样!"

"每当鸭子想要飞行的时候 🛫,它就去找 Flying 的虚函数表;想要游泳的时候 🏊,就去找 Swimming 的虚函数表。而最特别的是,它还有自己独特的 quack 函数,就像是英雄的必杀技一样 ⚡️!"

小王听得入迷了:"哇!所以说每个鸭子对象都带着两把钥匙 🔑,可以随时打开不同的技能宝箱?这设计也太巧妙了吧!"

"就是这样!" 老张开心地打了个响指 👆,"C++ 的多重继承就像是在玩超级英雄变身游戏 🎮,让我们的对象可以继承多方的超能力。虽然背后的实现很复杂,但使用起来却像魔法一样简单!" ✨

// 🦆 Duck 类的内存布局示意图
struct Duck_Layout {
    // 👋 第一部分: Flying 相关
    vptr_Flying* first_vptr;     // Flying的虚函数表指针
    // ... Flying的其他成员 ...

    // 🏊 第二部分: Swimming 相关
    vptr_Swimming* second_vptr;  // Swimming的虚函数表指针
    // ... Swimming的其他成员 ...

    // 🎯 第三部分: Duck自己的成员
    // ... Duck特有的成员变量 ...
};

// 🦅 Flying部分的虚函数表
struct Duck_VTable_Flying {
    // 定义函数指针类型
    typedef void (*FuncPtr)();
    
    // 存储虚函数的数组
    FuncPtr entries[3] = {
        // 👋 清理资源的析构函数
        (FuncPtr)&Duck::~Duck,    
        
        // ✈️ 起飞相关函数
        (FuncPtr)&Duck::takeOff,  
        
        // 🛬 着陆相关函数
        (FuncPtr)&Duck::land      
    };
};

// 🏊 Swimming部分的虚函数表
struct Duck_VTable_Swimming {
    // 定义函数指针类型
    typedef void (*FuncPtr)();
    
    // 存储虚函数的数组
    FuncPtr entries[4] = {
        // 🗑️ 析构函数(调整版本)
        (FuncPtr)&Duck::~Duck,   
        
        // 🏊 潜水功能
        (FuncPtr)&Duck::dive,    
        
        // 🌊 漂浮功能
        (FuncPtr)&Duck::float,   
        
        // 🔊 鸭子叫声(Duck特有)
        (FuncPtr)&Duck::quack    
    };
};

"这就像一栋双子大楼!" 老张兴奋地说 🏢,"每个基类都有自己的入口(vptr)和电梯(虚函数表),但它们都通向同一个顶层 - Duck 类的实现。而且最有趣的是,编译器会自动帮我们处理所有的指针转换和偏移计算!"

小王若有所思:"所以当我们用不同的基类指针指向 Duck 对象时..."

"没错!" 老张接着说 👍,"编译器会自动选择正确的 vptr 和偏移量。比如:"

// 创建一只可爱的鸭子 🦆
Duck duck;

// 使用 Flying 视角看鸭子 🦅
Flying* f = &duck;    

// 使用 Swimming 视角看鸭子 🏊
Swimming* s = &duck;  

// 让鸭子展翅高飞 ✈️
f->takeOff();  // 调用 Duck::takeOff

// 让鸭子深潜探索 🌊
s->dive();     // 调用 Duck::dive

"这就是为什么多重继承虽然强大,但也要小心使用," 老张总结道 🎯,"因为它会带来额外的内存开销和复杂性。每个基类都需要自己的 vptr,这意味着对象会变得更大,而且类型转换也可能带来一些性能开销。"

小王恍然大悟:"原来如此!这就像是在管理一个小型的商业综合体,每个部分都要有自己的管理系统,但最终都是为同一个整体服务。" 🏗️

"完全正确!" 老张笑着说 😊,"这就是 C++ 多重继承的魔法 - 复杂但强大,只要合理使用,就能创造出非常灵活的设计!"

虚继承又会带来哪些特殊的内存布局? 🔄

"说到虚继承," 老张喝了口咖啡,眼睛闪着光芒 ✨,"这可是 C++ 里最神奇的魔法之一了!想象一下,我们要解决著名的'钻石继承'问题..."

// 🦁 基类 Animal 定义
class Animal {
public:
    // 虚析构函数确保正确释放内存 🗑️
    virtual ~Animal() {} 
    
    // 动物的名字 📝
    std::string name;
};

// 🦅 飞行能力接口
class Flying :virtualpublic Animal {
public:
    // 飞行的虚函数 ✈️
    virtual void fly() { 
        std::cout << "飞翔中..." << std::endl; 
    }
};

// 🏊 游泳能力接口 
class Swimming :virtualpublic Animal {
public:
    // 游泳的虚函数 🌊
    virtual void swim() { 
        std::cout << "游泳中..." << std::endl; 
    }
};

// 🦆 鸭子类 - 继承飞行和游泳能力
class Duck :
    public Flying, 
    public Swimming {
public:
    // 鸭子特有的叫声 🔊
    void quack() { 
        std::cout << "嘎嘎!" << std::endl; 
    }
};

"在虚继承中," 老张拿起笔在白板上画起来 ✏️,"编译器就像一个超级聪明的魔法师 🧙♂️,它使用了一个特别巧妙的魔法咒语,确保我们心爱的 Animal 基类不会像被复制粘贴一样到处都是。想象一下,它就像是在一个豪华商场里 🏬,我们只建一个漂亮的中央大厅,然后所有的专卖店(派生类)都通过魔法传送门 🌀 直接连接到这个大厅,这样就不用在每个商店都重复建设接待区啦!"

老张眨眨眼睛继续说道:"编译器这个小机灵鬼会在对象的内存布局中偷偷放一个神奇的指针 🔮,就像给每个商店一把通向中央大厅的钥匙 🗝️。每当有人想要访问 Animal 的属性时,它就会顺着这个魔法指针,瞬间传送到那个独一无二的 Animal 实例那里。这样不管你是从飞行动物商店 🦅 还是游泳动物商店 🏊♂️ 进来,最终都能找到同一个温馨的家!"

"这简直就像是建造了一座充满魔法的空中花园 🌺,所有的派生类都能共享这片美丽的花园,而不是每个人都要辛苦地种一遍花草呢!" 老张笑着说,眼睛里闪烁着智慧的光芒 ✨。

// 🦆 Duck 类的完整内存布局图解 
struct Duck_Layout {
    // 💫 虚函数表指针区域
    vptr_Duck* main_vptr;        // 主控制台指针 🎮
    vptr_Flying* flying_vptr;    // 飞行能力指针 ✈️
    vptr_Swimming* swimming_vptr; // 游泳能力指针 🏊
    
    // 🔮 虚基类魔法区域
    Animal* vbptr;               // 动物基因指针 🧬
    
    // 🦅 飞行能力专属空间
    float wing_span;             // 翅膀展开长度
    int flight_speed;            // 飞行速度
    // ... 更多飞行相关属性 ...
    
    // 🌊 游泳能力专属空间
    float swim_speed;            // 游泳速度
    int dive_depth;              // 潜水深度
    // ... 更多游泳相关属性 ...
    
    // 🐾 共享的动物特征区域
    struct {
        std::string name;        // 动物名字 📝
        int age;                 // 动物年龄 🎂
        // ... 更多共享属性 ...
    } shared_animal;
};

"这就像建造一座超级智能大厦 🏢," 老张兴奋地说,"我们把共同的 Animal 部分放在一个特殊的位置,然后用虚基类指针来指向它。这样 Flying 和 Swimming 就可以共享同一个 Animal,就像共用一个大堂一样!"

"但是等等,这里有个有趣的细节," 老张神秘地眨眨眼 👀,"构造函数的调用顺序也变得特别有趣:"

Duck::Duck() {
    // 1. 首先构造虚基类 Animal
    Animal::Animal();
    
    // 2. 然后是直接基类
    Flying::Flying();
    Swimming::Swimming();
    
    // 3. 最后是自己的初始化
    // ... Duck 自己的初始化代码 ...
}

"这就像是建房子," 老张打了个比方 🏗️,"必须先把共用的底层大堂(Animal)建好,才能往上盖 Flying 和 Swimming 的楼层。而编译器就像一个细心的工程师,会自动帮我们安排好这些施工顺序!"

小王若有所思:"所以虚继承虽然解决了钻石继承的问题,但也带来了额外的内存开销和复杂性?"

"没错!" 老张点点头 👍,"每个虚继承都需要额外的虚基类指针,而且对象的布局也变得更复杂。这就是为什么我们要谨慎使用虚继承 - 它确实很强大,但也要付出相应的代价。"

"不过最神奇的是," 老张补充道 ✨,"所有这些复杂的内存布局和指针调整都是由编译器自动完成的。我们只需要声明 virtual 继承,编译器就会帮我们处理好所有的细节!"

"这就是 C++ 的魅力所在," 老张总结道 🌟,"它既给了我们强大的工具来解决复杂问题,又帮我们处理了所有繁琐的底层细节。就像是有一个贴心的助手,帮我们打理好一切!"

小王点点头:"原来如此!虚继承就像是在建造一座共享空间的智能大厦,虽然构造复杂,但确实解决了实际问题!" 🏢

"完全正确!" 老张笑着说 😊,"这就是为什么理解这些底层原理如此重要 - 它能帮助我们做出更明智的设计决策!"

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

2021-05-06 08:19:59

微信自动扣费腾讯

2017-06-06 11:37:29

2010-01-18 17:38:54

C++虚函数表

2021-08-05 12:51:11

摄像头视频攻击

2019-07-22 10:13:54

2020-11-30 10:05:57

大数据数据开发

2019-07-19 13:39:47

2009-03-18 17:00:24

IBMSUN收购

2022-07-18 15:32:37

C++虚函数表

2009-10-20 09:29:49

欧德宁高管内部交易

2010-06-22 10:11:16

富士康

2010-06-07 10:31:36

陈晓薇唐骏

2021-03-29 07:40:32

Swift Hook 虚函数表

2009-03-18 17:08:05

SunIBM收购

2020-08-11 10:25:38

数据成本数据大数据

2011-08-01 08:59:08

Email安全间谍软件

2023-03-02 08:19:43

不加锁程序实时性

2021-02-01 08:36:19

JS引擎V8

2018-12-07 11:15:20

设置Windows 10命令

2015-07-28 15:08:15

点赞
收藏

51CTO技术栈公众号