C++进阶2(多态)

C++进阶:详解多态(多态、虚函数、抽象类以及虚函数原理详解) 摘要: 多态是面向对象编程中令人着迷的概念,通过虚函数实现不同对象对同一消息的不同响应。C++11的`override`和`final`关键字增强了虚函数的重写机制。重载、覆盖和隐藏让代码更具灵活性和可扩展性。 抽象类为定义接口和规范派生类行为提供了有力工具。虚函数的原理涉及虚函数表,实现动态绑定,让程序更具扩展性。单继承和多继承中的虚函数表展现了面向对象编程的精妙之处。 阅读详情

目录
1.唠唠啥是多态(概念篇)
2.多态咋实现?(定义与实现篇)
3.抽象类是个啥?(抽象类篇)
4.多态背后的小秘密(原理篇)
5.单继承 / 多继承的虚表那些事(虚表实战篇)
6.面试常考!继承多态高频题(面试保命篇)

注意:
本篇笔记基于 VS2013 x86 环境整理,该环境下指针占 4 个字节;如果是 x64 环境,指针占 8 个字节,部分代码需要对应修改,学习时注意环境差异哦~

1. 唠唠啥是多态(概念篇)
1.1 通俗理解多态

多态,简单说就是同一个行为,不同对象去做,结果不一样,也就是 “多种形态”。
举几个新手能秒懂的例子:

  • 买票行为:普通人全价买,学生半价买,军人优先买,同样是 “买票”,不同人执行结果不同;
  • 支付宝扫红包:新用户能扫到大红包,老用户只能扫到小红包,同样是 “扫码”,不同用户结果不同。

1.2 编程中的多态
在 C++ 继承关系中,不同继承类的对象,调用同一个函数,产生不同的执行效果,这就是编程中的多态。比如 Student 类继承 Person 类,Person 对象调用 BuyTicket () 是全价,Student 对象调用 BuyTicket () 是半价。

2. 多态咋实现?(定义与实现篇)
想要实现多态,可不是随便写就行,得满足继承关系+两个核心条件,还得搞懂虚函数、重写这些基础概念,新手一步一步来~

2.1 多态的 3 个构成条件
1.存在继承关系(派生类继承基类);
2.必须通过基类的指针或者引用调用虚函数(记死:对象直接调用不行,必须指针 / 引用!);
3.被调用的函数是虚函数,且派生类重写了基类的这个虚函数。

2.2 虚函数:多态的 “开关”
**被 virtual 关键字修饰的类成员函数,就是虚函数,**这是实现多态的基础,没有虚函数就没有多态。

#include <iostream>
using namespace std;
// 基类Person
class Person {
public:
    // 虚函数:买票全价
    virtual void BuyTicket() {
        cout << "买票-全价" << endl;
    }
};

注意:virtual 只需要在函数声明时加,类外定义时不用加~

2.3 虚函数的重写:派生类的 “定制化”
虚函数的重写(也叫覆盖),简单说就是派生类写一个和基类虚函数 “一模一样” 的函数,把基类的虚函数功能替换掉。
3 个 “一模一样” 的要求
派生类虚函数和基类虚函数的返回值类型、函数名字、参数列表必须完全相同,少一个都不行!

// 派生类Student继承Person
class Student : public Person {
public:
    // 重写基类的虚函数BuyTicket
    virtual void BuyTicket() {
        cout << "买票-半价" << endl;
    }
};
// 测试多态:用基类引用调用虚函数
void Func(Person& p) {
    p.BuyTicket(); // 谁调用,执行谁的重写函数
}
int main() {
    Person mike; // 基类对象
    Student johnson; // 派生类对象
    Func(mike); // 执行Person::BuyTicket,输出:买票-全价
    Func(johnson); // 执行Student::BuyTicket,输出:买票-半价
    return 0;
}

新手注意点
派生类重写基类虚函数时,就算不加 virtual 关键字,也能构成重写(因为继承后基类虚函数的属性被保留了),但非常不建议这么写,代码可读性差,新手一定要养成加 virtual 的习惯!

重写的两个 “例外情况”(了解即可,面试偶尔考)
不是所有重写都要求 “一模一样”,这两个例外新手记个印象,不用死磕:
1.协变:基类虚函数返回基类指针 / 引用,派生类虚函数返回派生类指针 / 引用,也算重写;
示例:

class A{};
class B : public A {};
class Person {
public:
    virtual A* f() {return new A;}
};
class Student : public Person {
public:
    virtual B* f() {return new B;} // 协变,构成重写
};

**2.析构函数的重写:**基类和派生类析构函数名字不同,但只要基类析构函数是虚函数,派生类析构函数无论加不加 virtual,都算重写。
原因:编译器会把所有析构函数的名字统一处理成destructor,本质还是 “名字相同”。
新手必知:基类析构函数一定要写成虚函数,否则用基类指针释放派生类对象时,会析构不完整,造成内存泄漏!

class Person {
public:
    virtual ~Person() {cout << "~Person()" << endl;}
};
class Student : public Person {
public:
    virtual ~Student() {cout << "~Student()" << endl;}
};
int main() {
    Person* p1 = new Person;
    Person* p2 = new Student;
    delete p1; // 析构Person
    delete p2; // 先析构Student,再析构Person,析构完整
    return 0;
}

2.4 C++11 新关键字:防止重写写错(新手强烈推荐用)
新手写代码很容易把函数名、参数写错,导致无法构成重写,编译还不报错,运行结果不对才发现问题,巨麻烦!C++11 的override和final能帮我们在编译阶段就检查出错误,一定要用起来~

final:虚函数 “锁死”,不让派生类重写
修饰虚函数,表示这个虚函数不能再被任何派生类重写,写了就编译报错。

class Car {
public:
    virtual void Drive() final {} // 锁死Drive函数
};
class Benz : public Car {
public:
    // 错误:Drive被final修饰,不能重写,编译报错
    virtual void Drive() {cout << "Benz-舒适" << endl;}
};

override:检查是否真的重写了基类虚函数
修饰派生类的虚函数,**表示这个函数是用来重写基类虚函数的,**如果基类没有对应的虚函数,编译直接报错。

class Car {
public:
    virtual void Drive(){}
};
class Benz : public Car {
public:
    // override检查:基类有Drive虚函数,重写成功
    virtual void Drive() override {
        cout << "Benz-舒适" << endl;
    }
    // 错误:基类没有Run虚函数,override检查失败,编译报错
    virtual void Run() override {
        cout << "Benz-跑" << endl;
    }
};

2.5 新手易混:重载、重写、隐藏的区别(必背)
这三个概念是新手重灾区,面试必考,用一张表讲清楚,新手对着记就行:
在这里插入图片描述
3. 抽象类是个啥?(抽象类篇)
抽象类也叫接口类,是 C++ 中强制派生类重写虚函数的手段,新手可以理解为 “一个只有‘规则’,没有‘实现’的类”。

3.1 抽象类的定义
在虚函数的后面加上 = 0,这个函数就变成了纯虚函数,包含纯虚函数的类,就是抽象类。

class Car {
public:
    // 纯虚函数:只有声明,没有实现
    virtual void Drive() = 0;
};

3.2 抽象类的核心特点(记死)
1.**抽象类不能实例化对象:**直接定义抽象类对象会编译报错,比如Car c;是错误的;
2.派生类继承抽象类后,也不能实例化对象,除非重写了所有的纯虚函数;
3.只要有一个纯虚函数没重写,派生类还是抽象类。

class Car { // 抽象类
public:
    virtual void Drive() = 0;
};
// 派生类Benz重写纯虚函数Drive,不是抽象类,可以实例化
class Benz : public Car {
public:
    virtual void Drive() {
        cout << "Benz-舒适" << endl;
    }
};
// 派生类BMW重写纯虚函数Drive,不是抽象类,可以实例化
class BMW : public Car {
public:
    virtual void Drive() {
        cout << "BMW-操控" << endl;
    }
};
int main() {
    // Car c; // 错误:抽象类不能实例化
    Car* pBenz = new Benz; // 基类指针指向派生类对象,多态
    pBenz->Drive(); // 输出:Benz-舒适
    Car* pBMW = new BMW;
    pBMW->Drive(); // 输出:BMW-操控
    delete pBenz;
    delete pBMW;
    return 0;
}

3.3 接口继承和实现继承(理解即可)
C++ 中的继承分两种,不用死抠概念,知道区别就行:
1.实现继承:继承普通函数,派生类直接用基类函数的实现,比如继承void func() {cout << “hello”;},派生类直接调用就能输出 hello;
2.接口继承:继承虚函数 / 纯虚函数,派生类继承的是 “函数的接口(规则)”,不是实现,必须自己重写才能用,这也是抽象类的核心作用。
小建议:如果不需要实现多态,就不要把函数定义成虚函数,浪费资源~

4. 多态背后的小秘密(原理篇)
新手学到这里可能会问:为啥基类指针 / 引用调用虚函数,就能自动识别对象类型,执行对应的函数?答案就是虚函数表(简称虚表),这是多态的底层核心,新手不用搞懂底层编译原理,但必须理解虚表的基本概念!

4.1 虚函数表:存虚函数地址的 “数组”
核心概念

只要一个类有虚函数,编译器就会为这个类生成一个虚函数表,这个表是一个存储虚函数指针的数组,数组最后面通常放一个nullptr(空指针)作为结束标志。
同时,这个类的每个对象中,都会多一个虚表指针(__vfptr),这个指针占 4 字节(x86)/8 字节(x64),指向类的虚表。
简单说:类有虚表,对象有虚表指针,指针指向虚表。

新手必看示例:计算有虚函数的类对象大小

class Base {
public:
    virtual void Func1() {
        cout << "Func1()" << endl;
    }
private:
    int _b = 1; // int占4字节
};
int main() {
    Base b;
    cout << sizeof(b) << endl; // 输出8(x86环境)
    return 0;
}

**解释:**对象 b 的大小 = 虚表指针(4 字节)+ 成员变量_b(4 字节)= 8 字节,新手终于明白为啥有虚函数的对象会 “变大” 了吧~

4.2 派生类虚表的生成规则
派生类继承基类后,编译器会为派生类生成自己的虚表,规则超简单,分 3 步:
**1.拷贝:**先把基类虚表的内容,完整拷贝到派生类虚表中;
2.覆盖:如果派生类重写了基类的某个虚函数,用派生类自己的虚函数指针,替换掉派生类虚表中对应的基类虚函数指针(这就是 “重写 = 覆盖” 的底层原因);
**3.追加:**派生类自己新增的虚函数,按声明的顺序,依次追加到派生类虚表的最后面。

// 基类Base:有2个虚函数,1个普通函数
class Base {
public:
    virtual void Func1() {cout << "Base::Func1()" << endl;}
    virtual void Func2() {cout << "Base::Func2()" << endl;}
    void Func3() {cout << "Base::Func3()" << endl;} // 普通函数,不进虚表
private:
    int _b = 1;
};
// 派生类Derive:重写Func1,无新增虚函数
class Derive : public Base {
public:
    virtual void Func1() {cout << "Derive::Func1()" << endl;} // 重写Func1
private:
    int _d = 2;
};
  • 基类 Base 的虚表:Func1()指针 → Func2()指针 → nullptr;
  • 派生类 Derive 的虚表:Derive::Func1()指针(覆盖) → Base::Func2()指针(拷贝) → nullptr;
  • 普通函数 Func3 () 不进虚表,因为只有虚函数才会存到虚表中。

4.3 多态的底层实现(通俗理解)
回到最开始的买票例子,多态的底层就是基类指针 / 引用通过虚表指针,找到对应对象的虚表,再从虚表中找到对应的虚函数执行。
通俗点说:
1.基类指针 / 引用指向基类对象时,通过对象的虚表指针,找到基类的虚表,执行基类的虚函数;
2.基类指针 / 引用指向派生类对象时,通过对象的虚表指针,找到派生类的虚表,执行派生类重写后的虚函数。

这也是为什么必须用基类指针 / 引用:如果直接用对象调用,编译器在编译阶段就确定了要执行的函数(静态绑定),不会去查虚表,自然无法实现多态。

4.4 静态绑定和动态绑定(面试必考,秒懂)
绑定就是确定函数调用地址的过程,分两种,对应 C++ 的两种多态:
1.静态绑定(前期绑定 / 早绑定):编译阶段就确定函数的调用地址,直接调用,速度快。
例子:函数重载、普通对象调用函数(包括虚函数);
2.动态绑定(后期绑定 / 晚绑定):运行阶段才确定函数的调用地址,通过虚表查找,速度稍慢,但能实现多态。
例子:基类指针 / 引用调用虚函数。

void Func(Person* p) {
    p->BuyTicket(); // 动态绑定:运行时查虚表,实现多态
}
int main() {
    Person mike;
    mike.BuyTicket(); // 静态绑定:编译阶段确定调用Person::BuyTicket
    Func(&mike); // 动态绑定
    return 0;
}

4.5 新手必知的虚表小知识点(避坑)
1.虚函数不是存在虚表中,虚表中存的是虚函数的指针,虚函数和普通函数一样,都存在代码段;
2.虚表不是存在对象中,对象中存的是虚表指针,虚表本身存在代码段(常量区);
3.一个类的所有对象,共享同一个虚表(节省内存),每个对象只有自己的虚表指针。

5. 单继承 / 多继承的虚表那些事(虚表实战篇)
新手掌握了基础虚表概念后,再看看单继承和多继承中的虚表情况,这部分是面试重点,也是新手进阶的关键,跟着代码敲一遍就懂了~
5.1 单继承中的虚表(简单,新手易掌握)
单继承就是派生类只继承一个基类,虚表生成完全遵循之前的 “拷贝 - 覆盖 - 追加” 规则,需要注意的是:VS 编译器的监视窗口会隐藏派生类新增的虚函数,看起来虚表不完整,但实际是存在的,我们可以用代码打印出完整虚表。

#include <iostream>
using namespace std;
// 定义虚函数指针类型
typedef void(*VFPTR)();
// 打印虚表函数
void PrintVTable(VFPTR vTable[]) {
    cout << "虚表地址:" << vTable << endl;
    for (int i = 0; vTable[i] != nullptr; ++i) {
        printf("第%d个虚函数地址:0X%x -> ", i, vTable[i]);
        VFPTR f = vTable[i];
        f(); // 调用虚函数,看执行结果
    }
    cout << endl;
}
// 基类Base
class Base {
public:
    virtual void func1() {cout << "Base::func1" << endl;}
    virtual void func2() {cout << "Base::func2" << endl;}
private:
    int a;
};
// 派生类Derive:单继承Base,重写func1,新增func3、func4
class Derive : public Base {
public:
    virtual void func1() {cout << "Derive::func1" << endl;}
    virtual void func3() {cout << "Derive::func3" << endl;}
    virtual void func4() {cout << "Derive::func4" << endl;}
private:
    int b;
};
int main() {
    Base b;
    Derive d;
    // 取出对象的虚表指针(x86环境,取前4字节)
    VFPTR* vTableb = (VFPTR*)(*(int*)&b);
    VFPTR* vTabled = (VFPTR*)(*(int*)&d);
    // 打印基类和派生类的虚表
    cout << "Base的虚表:" << endl;
    PrintVTable(vTableb);
    cout << "Derive的虚表:" << endl;
    PrintVTable(vTabled);
    return 0;
}

**解释:**派生类虚表先覆盖 func1,再拷贝 func2,最后追加新增的 func3、func4,和我们的规则完全一致~

5.2 多继承中的虚表(稍复杂,理解核心即可)
多继承就是派生类继承多个基类,核心特点:派生类会有多个虚表指针,对应多个基类的虚表,派生类重写的虚函数,会覆盖所有基类虚表中对应的虚函数,派生类新增的虚函数,放在第一个继承的基类的虚表末尾。

#include <iostream>
using namespace std;
typedef void(*VFPTR)();
void PrintVTable(VFPTR vTable[]) {
    cout << "虚表地址:" << vTable << endl;
    for (int i = 0; vTable[i] != nullptr; ++i) {
        printf("第%d个虚函数地址:0X%x -> ", i, vTable[i]);
        VFPTR f = vTable[i];
        f();
    }
    cout << endl;
}
// 基类1:Base1
class Base1 {
public:
    virtual void func1() {cout << "Base1::func1" << endl;}
    virtual void func2() {cout << "Base1::func2" << endl;}
private:
    int b1;
};
// 基类2:Base2
class Base2 {
public:
    virtual void func1() {cout << "Base2::func1" << endl;}
    virtual void func2() {cout << "Base2::func2" << endl;}
private:
    int b2;
};
// 派生类Derive:多继承Base1、Base2,重写func1,新增func3
class Derive : public Base1, public Base2 {
public:
    virtual void func1() {cout << "Derive::func1" << endl;}
    virtual void func3() {cout << "Derive::func3" << endl;}
private:
    int d1;
};
int main() {
    Derive d;
    // 取出第一个基类Base1的虚表指针
    VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);
    // 取出第二个基类Base2的虚表指针(偏移Base1的大小)
    VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));
    // 打印两个虚表
    cout << "Base1的虚表(Derive):" << endl;
    PrintVTable(vTableb1);
    cout << "Base2的虚表(Derive):" << endl;
    PrintVTable(vTableb2);
    return 0;
}

运行结果核心结论
1.Derive 有两个虚表指针,分别对应 Base1 和 Base2 的虚表;
2.Derive 重写的 func1,覆盖了 Base1 和 Base2 虚表中的 func1;
3.Derive 新增的 func3,放在第一个基类 Base1 的虚表末尾。

5.3 菱形继承 / 菱形虚拟继承(了解即可)
实际开发中不建议设计菱形继承 / 菱形虚拟继承,因为逻辑太复杂,容易出问题,还会有性能损耗,虚表的结构也会变得非常复杂,新手不用深入研究,知道 “实际开发不用,面试一般不考” 就行~

6. 面试常考!继承多态高频题(面试保命篇)
这部分是 C++ 面试的高频考点,新手一定要背会答案,理解解题思路,遇到类似题就能秒答~
6.1 选择题

  • 下面哪种面向对象的方法可以让你变得富有 ( )
    A: 继承 B: 封装 C: 多态 D: 抽象
    答案:A(解析:继承家产,新手趣味记忆~)
  • () 是面向对象程序设计语言中的一种机制,实现了方法的定义与具体的对象无关,而对方法的调用则可以关联于具体的对象 ()
    A: 继承 B: 模板 C: 对象的自身引用 D: 动态绑定
    答案:D(解析:动态绑定就是运行时根据对象类型调用函数,对应多态)
  • 面向对象设计中的继承和组合,说法错误的是 ( )
    A: 继承是静态复用,白盒复用;B: 组合是动态复用,黑盒复用;C: 优先使用继承,而不是组合;D: 继承破坏父类封装性
    答案:C(解析:设计原则:优先使用组合,而不是继承)
  • 以下关于纯虚函数的说法,正确的是 ( )
    A: 声明纯虚函数的类不能实例化对象;B: 声明纯虚函数的类是虚基类;C: 子类必须实现基类的纯虚函数;D: 纯虚函数必须是空函数
    答案:A(解析:B 虚基类是虚拟继承的概念;C 子类不实现就是抽象类;D 纯虚函数没有实现,不是空函数)
  • 关于虚函数的描述正确的是 ( )
    A: 派生类虚函数与基类参数个数 / 类型不同;B: 内联函数不能是虚函数;C: 派生类必须重新定义基类虚函数;D: 虚函数可以是 static 型
    答案:B(解析:A 重写要求参数完全相同;C 派生类可以不重写,继承基类的;Dstatic 函数没有 this 指针,不能进虚表)
  • 关于虚表说法正确的是 ( )
    A: 一个类只能有一张虚表;B: 子类无重写则与基类共用虚表;C: 虚表运行时动态生成;D: 一个类的不同对象共享该类的虚表
    答案:D(解析:A 多继承有多个虚表;B 子类有自己的虚表,只是内容拷贝基类;C 虚表编译阶段生成)
  • 假设 A 类有虚函数,B 继承 A 并重新 A 的虚函数,无自定义虚函数,则 ( )
    A:A 对象前 4 字节是虚表地址,B 不是;B: 都是虚基表地址;C: 二者虚表地址相同;D: 虚函数个数相同,虚表不同
    答案:D(解析:B 重写了 A 的虚函数,虚表内容不同,所以是不同的虚表)
  • 菱形虚拟继承的构造函数执行顺序题,答案:A(解析:虚拟继承中,基类构造函数只执行一次,先执行最顶层基类,再执行派生类)
  • 多继承指针偏移问题,答案:C(解析:派生类对象中,第一个基类指针和派生类指针地址相同,第二个基类指针有偏移)
  • 虚函数默认参数题,答案:B(解析:虚函数的默认参数由基类决定,派生类重写不改变默认参数,本题执行 B::func,参数用基类的 1)

6.2 问答题

  • 什么是多态?
    答:同一行为,不同对象执行产生不同的结果;在继承中,基类指针 / 引用调用虚函数,不同派生类对象执行不同的重写函数。
  • 重载、重写、隐藏的区别?
    答:重载是同一作用域,函数名相同参数不同;重写是基类 + 派生类,虚函数三要素相同;隐藏是基类 + 派生类,函数名相同但不构成重写。
  • 多态的实现原理?
    答:基于虚函数表和动态绑定;类有虚函数则生成虚表,对象有虚表指针指向虚表;基类指针 / 引用通过虚表指针找到对应对象的虚表,执行虚函数,实现动态绑定。
  • inline 函数可以是虚函数吗?
    答:可以,但编译器会忽略 inline 属性,因为虚函数需要放进虚表,无法内联。
  • 静态成员可以是虚函数吗?
    答:不能,静态成员函数没有 this 指针,无法访问虚表指针,不能放进虚表。
  • 构造函数可以是虚函数吗?
    答:不能,对象的虚表指针是在构造函数的初始化列表阶段初始化的,构造函数执行前,虚表指针还不存在。
  • 析构函数可以是虚函数吗?什么场景下用?
    答:可以,且基类析构函数必须定义为虚函数;当用基类指针 / 引用释放派生类对象时,能保证派生类和基类的析构函数都执行,避免内存泄漏。
  • 对象访问普通函数快还是虚函数更快?
    答:普通对象调用,速度一样;基类指针 / 引用调用,普通函数更快,因为虚函数需要运行时查虚表(动态绑定)。
  • 虚函数表是在什么阶段生成的,存在哪?
    答:编译阶段生成,存在代码段(常量区)。
  • 什么是抽象类?抽象类的作用?
    答:包含纯虚函数的类是抽象类,不能实例化;作用是强制派生类重写虚函数,体现接口继承关系。
单细胞数据分析实战:用scDblFinder快速搞定异质性双细胞检测(附完整R代码) 本文详细介绍了如何使用scDblFinder工具在单细胞RNA测序数据中精准识别异质性双细胞。通过完整的R代码示例,从环境准备、数据加载到核心分析流程和结果可视化,帮助研究人员高效解决双细胞检测问题,提升单细胞数据分析的准确性。scDblFinder的集成算法和鲁棒性设计使其成为处理单细胞数据中技术噪音的理想选择。 阅读详情

相关推荐

大模型运维实战:从GPU选型到推理服务稳定落地

大模型服务进入生产环境后,运维对象从传统进程转向GPU显存、KV Cache、推理吞吐等动态资源。显存利用率、首token延迟、并发队列深度等指标,直接影响模型服务的质量与成本。理解推理框架的内存分配机制、掌握监控告警与自动化扩容手段,是保障服务稳定的关键。这类能力广泛应用于企业私有化部署、智能客服、知识库问答等场景,帮助运维团队从被动救火转向主动容量规划与性能调优。本文围绕大模型运维的完整路线,梳理从硬件选型、推理部署到监控排障的实战经验,并算清一份真实项目的长期成本账。

weixin_29054245的博客 238

C++进阶知识2 多态

1. 多态的概念 2. 多态的定义及实现 2.1 多态的构成条件 2.1.2 虚函数 2.1.3 虚函数的重写/覆盖 2.1.5 虚函数重写的⼀些其他问题 2.1.6 override和final关键字 2.1.7 重载/重写/隐藏的对⽐ 3. 多态的原理 3.2 多态的原理 3.2.1 多态是如何实现的 3.2.2 动态绑定与静态绑定 3.2.3 虚函数表

Yusei_0523的博客 1140

阿里云Ubuntu部署Steam饥荒联机版服务器:从零到多人联机的完整指南

本文详细介绍了在阿里云Ubuntu系统上部署Steam饥荒联机版服务器的完整流程,包括环境准备、SteamCMD安装、服务端配置、Mod管理及后台运行技巧。通过2核4GB的ECS实例配置和端口设置,实现5人流畅联机体验,适合游戏爱好者快速搭建专属服务器。

weixin_29229261的博客 1024

C++进阶-多态2

本文详细讲解了C++多态的实现原理和使用方法。主要内容包括:1.纯虚函数和抽象类的定义与使用,说明纯虚函数强制派生类重写虚函数的作用;2.多态的实现原理,重点分析虚函数表指针(_vfptr)和虚函数表的结构,解释动态绑定的概念;3.通过具体代码示例演示多态的实现过程,包括基类和派生类虚函数表的差异;4.对比重载、重写和隐藏三者的区别;5.总结多态的核心思想和使用要点。文章通过调试结果和内存分析,深入探讨了虚函数表在运行时的作用机制,帮助读者理解多态在C++中的底层实现原理。

博客是在C++/Java/Liunx/算法的学习阶段所写的博客,内容可能不是很全面 864

创意视觉项目实战:用ESP32-S3+OV5640构建智能图像流系统

本文详细介绍了如何使用ESP32-S3开发板与OV5640摄像头模块构建智能图像流系统。从硬件连接到软件配置,涵盖Arduino环境搭建、图像采集优化、WiFi视频流传输及外设集成,为开发者提供完整的实战指南,助力智能家居和机器人视觉项目开发。

tech5的博客 1292

手把手教你学Simulink——基于传统电网建模与稳定性分析的场景实例:基于同步发电机的单机无穷大系统建模与稳定性分析

本文详细介绍了使用MATLAB/Simulink进行单机无穷大系统(SMIB)建模与稳定性分析的方法。教程从系统结构设计入手,包含同步发电机、输电线路、无穷大母线等核心模块的建模步骤和参数配置示例。重点讲解了七个关键步骤:项目创建、模块配置、控制系统集成、负载添加(可选)以及仿真运行与结果分析,并提供了具体的MATLAB代码示例。最后通过性能评估指标(功角差、电压跌落、频率偏差)分析系统稳定性,为电力系统动态特性研究提供了实用指导,强调准确参数设置对仿真结果的重要性。

MHD0815的博客 298

C++进阶 | [2] 多态

摘要:多态的概念,多态的条件,虚函数的重写,抽象类,多态的原理,虚函数与虚函数表,与多态有关的问答题

fantastic_13_7的博客 1102

运维工程师面试整理-安全常见安全漏洞及修复

暴露在互联网的服务和端口可能会成为攻击者的目标,例如开放的SSH、FTP、Telnet等。:攻击者通过注入恶意脚本,使其在其他用户的浏览器中执行,从而窃取用户信息或劫持用户会话。:通过插入恶意SQL代码,攻击者可以操纵应用程序的数据库查询,执行未授权的数据库操作。:展示你对新兴安全威胁和防护技术的了解和学习能力,如零信任架构、基于AI的安全分析等。在不同的服务器或沙盒环境中处理和存储上传的文件,避免在Web根目录下保存可执行文件。采用安全框架和标准(如OWASP、NIST),确保系统和应用的安全合规性。

不务正业的猿 924

【C++进阶】第2课—多态

多态是一个继承关系下的类的对象,去调用同一个函数,产生了不同的行为。

weixin_70620792的博客 1004

基于Simulink的风电系统背靠背变流器解耦控制仿真

本文完成了基于 Simulink 的风电背靠背变流器解耦控制仿真,实现了:✅掌握 dq 解耦控制的数学本质✅构建 MSC + GSC 完整控制链✅验证其在动态、扰动下的卓越性能✅达成高解耦度、低 THD、稳母线三大目标。

xiaoheshang_123的博客 79

S7-200SMART 中如何实现单按钮启停功能(两种方法)?

S7-200SMART 中如何实现单按钮启停功能(两种方法),Toggle库指令,状态翻转

Robot_PLC_HMI_Automation 1万+

C++进阶(2)——多态

文章摘要:本文详细讲解了C++多态的实现原理,重点分析了运行时多态(动态多态)的实现机制。通过虚函数、虚函数表等概念,阐述了继承体系下派生类对象调用同一函数产生不同行为的原理。文章对比了重载、重写和隐藏的区别,介绍了override和final关键字的使用,并探讨了虚函数表在单继承、多继承及菱形继承情况下的分布规律。最后通过内存地址验证了虚函数表通常存放在代码段的特性。本文包含大量代码示例和测试结果,帮助读者深入理解C++多态的实现细节。

2301_80065652的博客 3868

RK3588 部署 YOLOv5s 实战:多线程加速从 16FPS 飙升到 120FPS(含性能优化)

因为RK3588包含了3个NPU核心,支持3个核心同时工作、双核合作、以及单核工作。我们可以使用线程池来加速。线程池是一种长街的并发编程模型,它用于管理和复用线程,以提高多线程程序的性能和效率。线程池通常包含以下主要组件:1、任务队列:任务队列用于存储待执行的任务。这些任务可以是函数、方法或其他需要多线程环境中执行的操作。2、线程池管理:线程池管理器负责创建、销毁和管理线程。它会监视任务队列中的任务,根据需要创建线程,执行任务,并在任务完成后将线程放回池中以供重用。

FJN110的博客 99

C++进阶:2_多态

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。那么在继承中要构成多态还有两个条件必须通过基类的指针或者引用调用虚函数被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写**在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。**派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。

Open__Ai的博客 1375

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

C++进阶(2):多态

C++多态知识点

ffyhyh的博客 1049

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

C++进阶2:多态从概念到原理

你有没有写过这样的代码:定义了一个Animal类,里面有个bark()函数;又写了Cat和Dog类继承自Animal,各自实现bark()—— 但调用的时候,用Animal类型的指针指向Cat对象,调用的却是Cat的bark()(输出 “喵喵”);指向Dog对象,调用的是Dog的bark()(输出 “汪汪”)。这种 “同一个函数调用,不同对象表现出不同行为” 的特性,就是 C++ 面向对象的核心 ——多态。它让代码更灵活、更易扩展,是 “代码复用 + 功能定制” 的关键武器。

chen060603的博客 693

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

C++进阶(2)多态

多态

2301_79907403的博客 403

C/C++ 进阶(2)多态

1、必须通过基类的指针或者引用调用虚函数。2、被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。public:// 用virtual修饰的函数cout

敲代码嘞~的博客 842

【C++进阶】2.继承与多态详解

子类虚函数表继承父类的虚函数表后,有虚函数,会在生成一个虚函数,并覆盖父类继承下来的地址,因此子类虚函数表包含:继承父类的、子类重写的、子类生成的地址。子类的虚函数表先会继承父类的虚函数表,但和父类的虚函数表不是同一个(地址不同),可以类比于父类和子类的元素,名字相同,但不是同一个。如果没有构造函数,想用编译器生成的,那也要在初始化列表写空括号,此时自定义类型调用默认构造,内置类型随机。运行时,根据指向对象的虚函数表中找到虚函数地址,进行调用,因此,只有在运行时才能确定地址。

m0_68617924的博客 992

C++进阶篇2---多态

C++进阶篇2 --- 多态

V_zjs的博客 867

C++进阶必备:继承与多态的通关秘籍(2):多态详解!!

本文系统讲解了C++多态的核心概念与实现原理。首先区分编译时多态(函数重载)和运行时多态(虚函数机制),重点阐述动态多态的实现条件:必须通过基类指针/引用调用虚函数,且派生类需完成虚函数重写。文章详细解析了虚函数表机制,指出多态本质是通过虚表指针实现动态绑定,并对比了重载、重写、隐藏的差异。最后介绍了纯虚函数、抽象类等进阶概念,以及override/final关键字的使用场景。全文通过丰富代码示例,深入浅出地揭示了多态在继承体系中的底层运作原理。

liriying的博客 642

【C++进阶2--多态】面向对象三大特性之一,多种形态像魔法?

今天,带来C++多态的讲解。多态和继承并用,能产生“魔法般的效果”。*文中不足错漏之处望请斧正!

BaconZzz的博客 613

【C++进阶】多态要这样学才够清楚

深入理解多态,一步到位

小羊的博客 2974

C++进阶-多态

【C++进阶学习】C++中的多态零、前言一、多态的概念和定义二、虚函数1、概念和定义2、虚函数重写的特例3、C++11 override 和 final4、重载/重写/重定义对比三、抽象类四、多态的原理1、虚函数表2、多态的原理3、动态绑定与静态绑定4、多继承虚函数表五、继承和多态常见的面试问题 零、前言 C++有五大特性:对象,封装,继承,抽象和多态。而本章则将学习讲解C++中的多态 一、多态的概念和定义 概念: 通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同

~ 2219

★ C++进阶篇 ★ 多态

★ C++进阶篇 ★ 多态

2302_80328146的博客 1819

[cpp进阶]多态和虚函数

文章目录多态的概念 多态的概念 多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。 多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。 C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。 比如说: 买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人买票时是优先买票。 买票这一个行为,不同的人做就有不同的行为和结果,这就是多态。 ...

weixin_53027918的博客 1244
上一篇: C++1进阶(继承)
下一篇: 初阶linux2环境基础开发工具完整教程
code_whiter
博客等级 码龄2年 289粉丝 50原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值