当前位置: 首页 > news >正文

免费做链接的网站无线网络优化工程师

免费做链接的网站,无线网络优化工程师,长沙的网站建设公司,广州手机网站建设哪家好深入探索C多态:静态与动态绑定的奥秘 一. 多态1.1 定义1.2 多态定义及实现1.2.1 多态构成条件1.2.1.1 实现多态两个必要条件1.2.1.2 虚函数1.2.1.3 虚函数的重写/覆盖1.2.1.4 协变1.2.1.5 析构函数重写1.2.1.6 override和final关键字1.2.1.7 重载/重写/隐藏的对⽐ 1…

深入探索C++多态:静态与动态绑定的奥秘

  • 一. 多态
    • 1.1 定义
    • 1.2 多态定义及实现
      • 1.2.1 多态构成条件
        • 1.2.1.1 实现多态两个必要条件
        • 1.2.1.2 虚函数
        • 1.2.1.3 虚函数的重写/覆盖
        • 1.2.1.4 协变
        • 1.2.1.5 析构函数重写
        • 1.2.1.6 override和final关键字
        • 1.2.1.7 重载/重写/隐藏的对⽐
    • 1.3 纯虚函数和抽象类
    • 1.4 多态原理
      • 1.4.1 虚函数表指针
      • 1.4.2 原理
      • 1.4.3 静态绑定与动态绑定
      • 1.4.4 虚函数表
  • 二. 最后

本文章介绍面向对象编程的三大特性中的多态之一。

💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!
👍点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!
🚀分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对 C++ 感兴趣的朋友,让我们一起进步!

一. 多态

1.1 定义

多态使得不同类型的对象可以通过相同的接口进行交互,而每个对象的具体行为是根据其自身类型来决定的,分为编译时多态和运行时多态。举个例子:对于普通人来说,票价全价,学生来说,票价半价,军人来说,优先买票等,指在根据不同的对象完成不同买票行为。
下面用示例代码来展现该行为(如下):

#include <iostream>
using namespace std;class Person
{
public:virtual void BuyTicket() { cout << "买票-全价" << endl; }
};class Student : public Person {
public:virtual void BuyTicket() { cout << "买票-打折" << endl; }
};void Func(Person* ptr)
{// 这里可以看到虽然都是Person指针Ptr在调用BuyTicket// 但是跟ptr没关系,而是由ptr指向的对象决定的。ptr->BuyTicket();
}int main()
{Person p1;Student s1;Func(&p1);Func(&s1);return 0;
}

结果如下:
在这里插入图片描述
通过传递不同的对象,来实现不同的行为,上述传递Person类和Student类,分别对应的行为是全价和半价。

1.2 多态定义及实现

1.2.1 多态构成条件

多态的实现原理通过虚函数表,虚表由虚函数构成,即 Virtual 修饰的函数,还需要虚表指针进行定位函数调用。

1.2.1.1 实现多态两个必要条件
  • 必须是基类的指针或引用调用虚函数
  • 被调用的函数必须是虚函数,并且派生类需对基类被调用的虚函数重写/覆盖
1.2.1.2 虚函数

注意:必须是成员函数,不是成员函数的报错示例:
在这里插入图片描述
error: 类声明的外部说明符无效。

定义:类成员函数被virtual修饰,这个成员函数被称为虚函数。

1.2.1.3 虚函数的重写/覆盖

定义:派生类有一个与基类完成相同的虚函数(即派生类函数与基类函数的返回值类型、函数名字,参数列表完全形同,缺一不可),称为派生类的虚函数重写了基类的虚函数。注意:派生类的虚函数不加 virtual 修饰,也构成重写,但是不规范,不建议使用。

  • 例题:
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;
}

以下程序输出结果是什么()
A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确

  • 解释:

调用func前面是A*类型,是基类的指针调用,派生类对该函数进行了重写,所以构成多态。指向派生类,所以调用派生类的func,而派生类B前面省略了virtual,编译器会将派生类调用的func修改成:virtual void func(int val = 1){ std::cout<<“B->”<< val <std::endl;}所以叫作对基类虚函数的重写。即B-1,所以答案是B。

1.2.1.4 协变

派生类重写基类虚函数时,与基类虚函数返回值类型不同时。简单点说就是:基类指针返回基类对象的指针或引用,派生类返回派生类对象的指针或引用,称为协变。

  • 示例代码:
class A {};
class B : public A {};class Person {
public:virtual A* BuyTicket()//返回基类指针对象{cout << "买票-全价" << endl;return nullptr;}
};class Student : public Person {
public:virtual B* BuyTicket()//返回派生类指针对象{cout << "买票-打折" << endl;return nullptr;}
};void Func(Person* ptr)
{ptr->BuyTicket();
}int main()
{Person ps;Student st;Func(&ps);Func(&st);return 0;
}

上述举了一个关于协变的例子。

1.2.1.5 析构函数重写

注意:需将对基类的析构函数进行重写。下面给个代码例子来解释:

class A
{
public:virtual ~A(){cout << "~A()" << endl;}
};class B : public A {
public:virtual ~B() override {cout << "~B()->delete:" << _p << endl;delete [] _p;}
protected:int* _p = new int[10];
};int main()
{A* p1 = new A;A* p2 = new B; //这是C++的静态绑定特性:非虚函数调用仅由指针的静态类型(此处为A * )决定。delete p1;delete p2;return 0;
}

注意:C++的静态绑定特性:非虚函数调用仅由指针的静态类型决定。当基类的析构函数没有被修饰为虚函数,进行静态绑定,非虚函数调用仅由指针的静态类型(A*)决定。下面的B对象的资源不会被释放,造成内存泄漏。。当基类的析构函数被修饰为虚函数,构成多态,运行时绑定p2指针指向B对象,调用B对象的析构函数,成功将B对象的资源释放。

所以建议:最好将基类的析构函数修饰为虚函数,避免内存泄漏。

1.2.1.6 override和final关键字
  • override:检查成员函数是否构成重写
  • final:被修饰的虚函数不能被重写
1.2.1.7 重载/重写/隐藏的对⽐
  1. 重载:两个函数在同一作用域,函数名相同,参数不同,参数类型或者个数不同,返回值不关心,即可构成重载。
  2. 重写:两个函数作用域在不同的作用域(一般是基类和派生类),函数名,参数,返回值必须全部相同,协变除外,且两个函数必须是虚函数,派生类可以不显示写virtual,但不建议。
  3. 隐藏:两个函数作用域在不同的作用域(一般是基类和派生类),两个函数名相同即可,其它的不关心,父类与派生类的成员变量名相同也构成隐藏。

1.3 纯虚函数和抽象类

  • 纯虚函数:在虚函数后面 加上=0,则这个函数成为纯虚函数。
    示例:
class Car
{
public:
virtual void Drive() = 0;
};
  • 抽象类:包含纯虚函数的类称为抽象类,派生类可以继承抽象类,对纯虚函数进行重写,完成不同的功能。
    示例代码:
class Car
{
public:
virtual void Drive() = 0;
};
class Benz :public Car
{
public:
virtual void Drive()
{
cout << "Benz-舒适" << endl;
}
};

1.4 多态原理

1.4.1 虚函数表指针

看看下面程序在32为程序的运行结果是什么?

  • 示例代码:
class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}virtual void Func2(){cout << "Func2()" << endl;}void Func3(){cout << "Func3()" << endl;}
protected:int _b = 1;char _ch = 'x';
};int main()
{Base b;//类内包含一张纯虚函数表,也就是函数指针数组cout << sizeof(b) << endl;return 0;
}

输出结果为12,因为里面还存在虚函数表指针,指针在32位机器占4字节。
如图:
在这里插入图片描述

1.4.2 原理

构成多态时,运行到指定的对象的虚表中确定对应的虚函数,不再在编译时绑定,称为静态绑定,这是称为动态绑定。

1.4.3 静态绑定与动态绑定

  • 静态绑定:静态绑定是指在程序编译时就确定了方法调用的具体实现。在静态绑定中,编译器根据对象的编译时类型来决定调用哪个方法或访问哪个成员。静态绑定通常发生在方法重载(method overloading)或成员变量访问的场景。
  • 动态绑定:动态绑定是指在程序运行时根据对象的实际类型来决定调用哪个方法或访问哪个成员。动态绑定通常发生在方法重写(method overriding)和多态(polymorphism)的场景中。

1.4.4 虚函数表

  1. 基类对象的虚函数表中存放基类所有的虚函数地址。不同类型的对象具有不同的虚函数表。
  2. 派生类虚函数表里的内容相对复杂,包括基类虚函数地址,派生类重写基类虚函数地址完成覆盖,派生类自己虚函数的地址。
  3. 虚函数表本质是一个存放虚函数指针的指针数组。
  4. 虚函数存在哪?存在代码段,虚函数地址存在虚函数表中。
  5. 虚函数表存在哪?这个C++并没有标准答案,VS存放在代码段(常量区)。

二. 最后

本文深入探讨了C++多态性的核心概念与实现机制,涵盖多态定义、虚函数、协变、析构函数重写、override/final关键字、重载/重写/隐藏对比、纯虚函数与抽象类,以及多态原理如虚函数表指针、静态/动态绑定等,是C++面向对象编程的进阶指南。

http://www.cadmedia.cn/news/12164.html

相关文章:

  • 政府单位网站建设方案书九个关键词感悟中国理念
  • 威海高区有没有建设局的网站搜索引擎优化的工具
  • 网站后端建设云盘搜索
  • 网站建设技术和销售工资莱芜seo
  • 网站建设和web前端一样吗百度一下你就知道搜索引擎
  • 网站制作需要多少费用排名优化公司哪家好
  • 新疆建设厅官网深圳seo排名哪家好
  • 软件公众号开发seo关键词排名优化如何
  • 建设网站招标文件新能源汽车公司
  • 机票便宜网站建设石家庄新闻网
  • 天津建设部网站首页网络营销典型案例
  • 衣柜推拉门图案设计网站seo商学院
  • 苍山网站建设网络营销与策划试题及答案
  • 建一个网站的技术解决方案google搜索排名优化
  • 邢台县教育局五库建设网站枸橼酸西地那非片
  • 网站平台建设投资费用清单我想接app纯注册推广单
  • 专门做租房的网站又一病毒来了比新冠可怕
  • 萝岗营销型网站建设上海好的seo公司
  • 镇江网站建设公司产品推广渠道有哪些方式
  • 做五金上哪个网站推广想开广告公司怎么起步
  • 深圳网站建设 湖南岚鸿安卓优化大师app
  • 客户关系管理案例经典seo的含义是什么意思
  • 网站的音乐怎么做的天津seo推广
  • 电子商务网站建设实训需求分析南京seo优化
  • 与网站建设有关的招标文件宁波网站推广哪家公司好
  • 网站建设定制开发价格如何在百度上投放广告
  • 政府门户网站信息建设自查百度新闻发布平台
  • 成品网站w灬源码1688永久网站百度seo找哪里
  • 怎么给网站建设提建议网络营销策略方案
  • 政府网站建设加强组织流量购买网站