• 【一起来学C++】————(7)多态


    1、多态定义

      多态是C++面向对象三大特性之一,多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。
    Person对象买票全价,Student对象买票半价。

    class Animal
    {
    public:
    	//Speak函数就是虚函数
    	//函数前面加上virtual关键字,变成虚函数,
    	//那么编译器在编译的时候就不能确定函数调用了。
    	virtual void speak()
    	{
    		cout << "动物在说话" << endl;
    	}
    };
    
    class Cat :public Animal
    {
    public:
    	void speak()
    	{
    		cout << "小猫在说话" << endl;
    	}
    };
    
    class Dog :public Animal
    {
    public:
    
    	void speak()
    	{
    		cout << "小狗在说话" << endl;
    	}
    
    };
    //我们希望传入什么对象,那么就调用什么对象的函数
    //如果函数地址在编译阶段就能确定,那么静态联编
    //如果函数地址在运行阶段才能确定,就是动态联编
    
    void DoSpeak(Animal & animal)
    {
    	animal.speak();
    }
    //
    //多态满足条件: 
    //1、有继承关系
    //2、子类重写父类中的虚函数
    //多态使用:
    //父类指针或引用指向子类对象
    
    void test01()
    {
    	Cat cat;
    	DoSpeak(cat);
    
    
    	Dog dog;
    	DoSpeak(dog);
    }
    
    
    int main() {
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    那么在继承中要构成多态还有两个条件:

    1. 必须通过基类的指针或者引用调用虚函数
    2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

    2、多态分类

    多态分为两类
    静态多态: 函数重载运算符重载属于静态多态,复用函数名
    动态多态: 派生类虚函数实现运行时多态
    静态多态和动态多态区别:
    ● 静态多态的函数地址早绑定 - 编译阶段确定函数地址
    ● 动态多态的函数地址晚绑定 - 运行阶段确定函数地址

    多态案例一计算器类

    案例描述: 分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类

    多态的优点:
    ● 代码组织结构清晰
    ● 可读性强
    ● 利于前期和后期的扩展以及维护

    示例:

    //普通实现
    class Calculator {
    public:
    	int getResult(string oper)
    	{
    		if (oper == "+") {
    			return m_Num1 + m_Num2;
    		}
    		else if (oper == "-") {
    			return m_Num1 - m_Num2;
    		}
    		else if (oper == "*") {
    			return m_Num1 * m_Num2;
    		}
    		//如果要提供新的运算,需要修改源码
    	}
    public:
    	int m_Num1;
    	int m_Num2;
    };
    
    void test01()
    {
    	//普通实现测试
    	Calculator c;
    	c.m_Num1 = 10;
    	c.m_Num2 = 10;
    	cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl;
    
    	cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl;
    
    	cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl;
    }
    
    
    
    //多态实现
    //抽象计算器类
    //多态优点:代码组织结构清晰,可读性强,利于前期和后期的扩展以及维护
    class AbstractCalculator
    {
    public :
    
    	virtual int getResult()
    	{
    		return 0;
    	}
    
    	int m_Num1;
    	int m_Num2;
    };
    
    //加法计算器
    class AddCalculator :public AbstractCalculator
    {
    public:
    	int getResult()
    	{
    		return m_Num1 + m_Num2;
    	}
    };
    
    //减法计算器
    class SubCalculator :public AbstractCalculator
    {
    public:
    	int getResult()
    	{
    		return m_Num1 - m_Num2;
    	}
    };
    
    //乘法计算器
    class MulCalculator :public AbstractCalculator
    {
    public:
    	int getResult()
    	{
    		return m_Num1 * m_Num2;
    	}
    };
    
    
    void test02()
    {
    	//创建加法计算器
    	AbstractCalculator *abc = new AddCalculator;
    	abc->m_Num1 = 10;
    	abc->m_Num2 = 10;
    	cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
    	delete abc;  //用完了记得销毁
    
    	//创建减法计算器
    	abc = new SubCalculator;
    	abc->m_Num1 = 10;
    	abc->m_Num2 = 10;
    	cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
    	delete abc;  
    
    	//创建乘法计算器
    	abc = new MulCalculator;
    	abc->m_Num1 = 10;
    	abc->m_Num2 = 10;
    	cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
    	delete abc;
    }
    
    int main() {
    
    	//test01();
    
    	test02();
    
    	system("pause");
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118

    3、纯虚函数和抽象类

    在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容因此可以将虚函数改为纯虚函数

    纯虚函数语法: virtual 返回值类型 函数名 (参数列表)= 0 ;
    
    • 1

    当类中有了纯虚函数,这个类也称为抽象类
    抽象类特点:
    ● 无法实例化对象
    ● 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
    示例:

    class Base
    {
    public:
    	//纯虚函数
    	//类中只要有一个纯虚函数就称为抽象类
    	//抽象类无法实例化对象
    	//子类必须重写父类中的纯虚函数,否则也属于抽象类
    	virtual void func() = 0;
    };
    
    class Son :public Base
    {
    public:
    	virtual void func() 
    	{
    		cout << "func调用" << endl;
    	};
    };
    
    void test01()
    {
    	Base * base = NULL;
    	//base = new Base; // 错误,抽象类无法实例化对象
    	base = new Son;
    	base->func();
    	delete base;//记得销毁
    }
    
    int main() {
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    虚函数表

    // 这里常考一道笔试题:sizeof(Base)是多少?
    class Base
    {
    public:
    virtual void Func1()
    {
    cout << "Func1()" << endl;
    }
    private:
    int _b = 1;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    通过观察测试我们发现b对象是8bytes,除了_b成员,还多一个__vfptr放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表function)。一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表。

    4、虚析构和纯虚析构

    多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

    解决方式:将父类中的析构函数改为虚析构或者纯虚析构

    虚析构和纯虚析构共性:
    ● 可以解决父类指针释放子类对象
    ● 都需要有具体的函数实现

    虚析构和纯虚析构区别:
    ● 如果是纯虚析构,该类属于抽象类,无法实例化对象

    虚析构语法:
    virtual ~类名(){}
    
    • 1
    • 2
    纯虚析构语法:
    virtual ~类名() = 0;
    类名::~类名(){}
    
    • 1
    • 2
    • 3

    示例:

    class Animal {
    public:
    
    	Animal()
    	{
    		cout << "Animal 构造函数调用!" << endl;
    	}
    	virtual void Speak() = 0;
    
    	//析构函数加上virtual关键字,变成虚析构函数
    	//virtual ~Animal()
    	//{
    	//	cout << "Animal虚析构函数调用!" << endl;
    	//}
    
    
    	virtual ~Animal() = 0;
    };
    
    Animal::~Animal()
    {
    	cout << "Animal 纯虚析构函数调用!" << endl;
    }
    
    //和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化。
    
    class Cat : public Animal {
    public:
    	Cat(string name)
    	{
    		cout << "Cat构造函数调用!" << endl;
    		m_Name = new string(name);
    	}
    	virtual void Speak()
    	{
    		cout << *m_Name <<  "小猫在说话!" << endl;
    	}
    	~Cat()
    	{
    		cout << "Cat析构函数调用!" << endl;
    		if (this->m_Name != NULL) {
    			delete m_Name;
    			m_Name = NULL;
    		}
    	}
    
    public:
    	string *m_Name;
    };
    
    void test01()
    {
    	Animal *animal = new Cat("Tom");
    	animal->Speak();
    
    	//通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏
    	//怎么解决?给基类增加一个虚析构函数
    	//虚析构函数就是用来解决通过父类指针释放子类对象
    	delete animal;
    }
    
    int main() {
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    总结:
    ​ 1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
    ​ 2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
    ​ 3. 拥有纯虚析构函数的类也属于抽象类

    6、多态案例三-电脑组装

    案例描述:
    电脑主要组成部件为 CPU(用于计算),显卡(用于显示),内存条(用于存储)
    将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商
    创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
    测试时组装三台不同的电脑进行工作
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例:

    #include
    using namespace std;
    
    //抽象CPU类
    class CPU
    {
    public:
    	//抽象的计算函数
    	virtual void calculate() = 0;
    };
    
    //抽象显卡类
    class VideoCard
    {
    public:
    	//抽象的显示函数
    	virtual void display() = 0;
    };
    
    //抽象内存条类
    class Memory
    {
    public:
    	//抽象的存储函数
    	virtual void storage() = 0;
    };
    
    //电脑类
    class Computer
    {
    public:
    	Computer(CPU * cpu, VideoCard * vc, Memory * mem)
    	{
    		m_cpu = cpu;
    		m_vc = vc;
    		m_mem = mem;
    	}
    
    	//提供工作的函数
    	void work()
    	{
    		//让零件工作起来,调用接口
    		m_cpu->calculate();
    
    		m_vc->display();
    
    		m_mem->storage();
    	}
    
    	//提供析构函数 释放3个电脑零件
    	~Computer()
    	{
    
    		//释放CPU零件
    		if (m_cpu != NULL)
    		{
    			delete m_cpu;
    			m_cpu = NULL;
    		}
    
    		//释放显卡零件
    		if (m_vc != NULL)
    		{
    			delete m_vc;
    			m_vc = NULL;
    		}
    
    		//释放内存条零件
    		if (m_mem != NULL)
    		{
    			delete m_mem;
    			m_mem = NULL;
    		}
    	}
    
    private:
    
    	CPU * m_cpu; //CPU的零件指针
    	VideoCard * m_vc; //显卡零件指针
    	Memory * m_mem; //内存条零件指针
    };
    
    //具体厂商
    //Intel厂商
    class IntelCPU :public CPU
    {
    public:
    	virtual void calculate()
    	{
    		cout << "Intel的CPU开始计算了!" << endl;
    	}
    };
    
    class IntelVideoCard :public VideoCard
    {
    public:
    	virtual void display()
    	{
    		cout << "Intel的显卡开始显示了!" << endl;
    	}
    };
    
    class IntelMemory :public Memory
    {
    public:
    	virtual void storage()
    	{
    		cout << "Intel的内存条开始存储了!" << endl;
    	}
    };
    
    //Lenovo厂商
    class LenovoCPU :public CPU
    {
    public:
    	virtual void calculate()
    	{
    		cout << "Lenovo的CPU开始计算了!" << endl;
    	}
    };
    
    class LenovoVideoCard :public VideoCard
    {
    public:
    	virtual void display()
    	{
    		cout << "Lenovo的显卡开始显示了!" << endl;
    	}
    };
    
    class LenovoMemory :public Memory
    {
    public:
    	virtual void storage()
    	{
    		cout << "Lenovo的内存条开始存储了!" << endl;
    	}
    };
    
    
    void test01()
    {
    	//第一台电脑零件
    	CPU * intelCpu = new IntelCPU;
    	VideoCard * intelCard = new IntelVideoCard;
    	Memory * intelMem = new IntelMemory;
    
    	cout << "第一台电脑开始工作:" << endl;
    	//创建第一台电脑
    	Computer * computer1 = new Computer(intelCpu, intelCard, intelMem);
    	computer1->work();
    	delete computer1;
    
    	cout << "-----------------------" << endl;
    	cout << "第二台电脑开始工作:" << endl;
    	//第二台电脑组装
    	Computer * computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);
    	computer2->work();
    	delete computer2;
    
    	cout << "-----------------------" << endl;
    	cout << "第三台电脑开始工作:" << endl;
    	//第三台电脑组装
    	Computer * computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);
    	computer3->work();
    	delete computer3;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169

    7、继承和多态常见的面试问题

    1 概念查考

    1. 下面哪种面向对象的方法可以让你变得富有( )
      A: 继承 B: 封装 C: 多态 D: 抽象
    2. ( )是面向对象程序设计语言中的一种机制。这种机制实现了方法的定义与具体的对象无关,而对方法的
      调用则可以关联于具体的对象。
      A: 继承 B: 模板 C: 对象的自身引用 D: 动态绑定
    3. 面向对象设计中的继承和组合,下面说法错误的是?()
      A:继承允许我们覆盖重写父类的实现细节,父类的实现对于子类是可见的,是一种静态复用,也称为
      白盒复用
      B:组合的对象不需要关心各自的实现细节,之间的关系是在运行时候才确定的,是一种动态复用,也
      称为黑盒复用
      C:优先使用继承,而不是组合,是面向对象设计的第二原则
      D:继承可以使子类能自动继承父类的接口,但在设计模式中认为这是一种破坏了父类的封装性的表现
    4. 以下关于纯虚函数的说法,正确的是( )
      A:声明纯虚函数的类不能实例化 B:声明纯虚函数的类成虚基类
      C:子类必须实现基类的 D:纯虚函数必须是空函数
    5. 关于虚函数的描述正确的是( )
      A:派生类的虚函数与基类的虚函数具有不同的参数个数和类型 B:内联函数不能是虚函数
      C:派生类必须重新定义基类的虚函数 D:虚函数可以是一个static型的函数
    6. 关于虚表说法正确的是( )
      A:一个类只能有一张虚表
      B:基类中有虚函数,如果子类中没有重写基类的虚函数,此时子类与基类共用同一张虚表
      C:虚表是在运行期间动态生成的
      D:一个类的不同对象共享该类的虚表
    7. 假设A类中有虚函数,B继承自A,B重写A中的虚函数,也没有定义任何虚函数,则( )
      A:A类对象的前4个字节存储虚表地址,B类对象前4个字节不是虚表地址
      B:A类对象和B类对象前4个字节存储的都是虚基表的地址
      C:A类对象和B类对象前4个字节存储的虚表地址相同
      D:A类和B类中的内容完全一样,但是A类和B类使用的不是同一张虚表
    8. 下面程序输出结果是什么? ()
    #include
    using namespace std;
    class A{
    public:
    A(char *s) { cout<<s<<endl; }
    ~A(){}
    };
    class B:virtual public A
    {
    public:
    B(char *s1,char*s2):A(s1) { cout<<s2<<endl; }
    };
    class C:virtual public A
    {
    public:
    C(char *s1,char*s2):A(s1) { cout<<s2<<endl; }
    };
    class D:public B,public C
    {
    public:
    D(char *s1,char *s2,char *s3,char *s4):B(s1,s2),C(s1,s3),A(s1)
    { cout<<s4<<endl;}
    };
    int main() {
    D *p=new D("class A","class B","class C","class D");
    delete p;
    return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    A:class A class B class C class D B:class D class B class C class A
    C:class D class C class B class A D:class A class C class B class D

    1. 多继承中指针偏移问题?下面说法正确的是( )
    class Base1 { public: int _b1; };
    class Base2 { public: int _b2; };
    class Derive : public Base1, public Base2 { public: int _d; };
    int main(){
    Derive d;
    Base1* p1 = &d;
    Base2* p2 = &d;
    Derive* p3 = &d;
    return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    A:p1 == p2 == p3 B:p1 < p2 < p3 C:p1 == p3 != p2 D:p1 != p2 != p3
    10. 以下程序输出结果是什么()

    class A
    {
    public:
    virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}
    virtual void test(){ func();}
    };
    class B : public A
    {
    public:
    void func(int val=0){ std::cout<<"B->"<< val <<std::endl; }
    };
    int main(int argc ,char* argv[])
    {
    B*p = new B;
    p->test();
    return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确

    2 问答题

    1. 什么是多态?答:参考本节课件内容

    2. 什么是重载、重写(覆盖)、重定义(隐藏)?
      答:在这里插入图片描述

    3. 多态的实现原理?答:参考本节课件内容

    4. inline函数可以是虚函数吗?答:不能,因为inline函数没有地址,无法把地址放到虚函数表中。

    5. 静态成员可以是虚函数吗?答:不能,因为静态成员函数没有this指针,使用类型::成员函数的调用方式
      无法访问虚函数表,所以静态成员函数无法放进虚函数表。

    6. 构造函数可以是虚函数吗?答:不能,因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始
      化的。

    7. 析构函数可以是虚函数吗?什么场景下析构函数是虚函数?答:可以,并且最好把基类的析构函数定义
      成虚函数。参考本节课件内容

    8. 对象访问普通函数快还是虚函数更快?答:首先如果是普通对象,是一样快的。如果是指针对象或者是
      引用对象,则调用的普通函数快,因为构成多态,运行时调用虚函数需要到虚函数表中去查找。

    9. 虚函数表是在什么阶段生成的,存在哪的?答:虚函数表是在编译阶段就生成的,一般情况下存在代码
      段(常量区)的。

    10. C++菱形继承的问题?虚继承的原理?

  • 相关阅读:
    消费力回落,名创优品大涨:深耕兴趣消费实现逆势增长
    【Vue五分钟】 五分钟了解webpack的核心概念
    (五):jenkins Node JS插件安装+配置
    【globlal与nonlocal和闭包函数、装饰器、语法糖】
    基于形态学处理的交通标志检测分割算法matlab仿真
    malloc如何分配内存
    【技巧】如何保护PDF文件不被随意修改?
    如何制作HTML网页设计【体育运动主题网站——中国篮球NBA】
    CocosCreator 面试题(一)Javascript的垃圾回收机制
    godot引擎学习2
  • 原文地址:https://blog.csdn.net/qq_38364548/article/details/126091482