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

《C++ 多态》

目录

实现多态的重要条件

静态绑定和动态绑定

重载,重写,隐藏

变态题

析构函数的重写

虚函数表


实现多态的重要条件

基类对象的指针或引用在调用同一函数时产生了不同的效果,这就是多态。

1.必须是基类的指针或引用调用虚函数。

2.虚函数必须在派生类中完成了重写。

静态绑定和动态绑定

1.对于不满足多态的函数调用,程序在编译时就确定了函数调用的地址,完成了函数参数的绑定,就是静态绑定。

2.对于满足多态的函数调用,程序会在运行时去虚函数表寻找函数的地址调用,就是动态绑定。

重载,重写,隐藏

在相同作用域中,函数名相同,参数个数或类型不同的函数,构成重载

在基类域和派生类域中函数名相同,参数相同,返回值相同的函数,构成重写

在基类域和派生类域中函数名相同,成员变量名相同,派生类的函数/变量对基类的构成隐藏

变态题

答案是"B->1"。

解析:

派生类B对象的指针p指向堆上开辟的B的对象。

p调用基类A中的虚函数test,在A::test()内部调用func()时,检查func()的声明发现是虚函数,但是默认参数val=1在编译时直接嵌入调用处,完成静态绑定,代码等价于this->func(1)。

但是p是派生类B对象的指针,也就是说这个this是B*的切片A*指向的是派生类对象中基类的那一部分,这里B中重写了func函数,所以调用的是B重写的函数部分

//下面程序的运行结果是什么
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;
}

析构函数的重写

下面这段程序运行后不会调用B中的析构函数,造成内存泄露。

原因是编译器会在编译时将析构函数名称都改为destructor此时基类A和派生类B的析构函数是隐藏关系。在delete p2时调用析构函数,p2是基类的指针,本来这里的目的是实现多态让p2调用B的析构函数,但是这里析构函数不是虚函数所以无法通过虚函数表找到B的析构函数的地址,所以只会根据静态绑定调用A的析构函数,造成内存泄漏

正确做法是将基类的析构函数置为虚函数,这样p2调用析构函数(同时也因为p2指向的是派生类对象的基类那一部分)就会通过虚函数表找到B的析构函数去调用,再自动调用A的析构函数。

class A
{
public://virtual ~A()~A(){cout << "~A()" << endl;}
};
class B : public A {
public:~B(){cout << "~B()->delete:" << _p << endl;delete _p;}
protected:int* _p = new int[10];
};int main()
{A* p1 = new A;A* p2 = new B;//delete的原理//p1->~destructor operator deletedelete p1;delete p2;return 0;
}

虚函数表

下面的程序输出是8,这是因为除了成员变量a外,打开监视窗口会发现还有一个成员_vfptr,这是一个虚函数表虚函数表本质是一个指针数组,这个指针数组中存放的是虚函数的指针。

基类的虚函数表会存放基类的虚函数指针;派生类的虚函数表存放基类的虚函数指针,派生类重写的虚函数指针会覆盖基类的虚函数指针,派生类虚函数指针。派生类的基类和原始的基类是不同的。

 

class Base{
public:
virtual void func1() { cout << "Base::func1" << endl; }
virtual void func2() { cout << "Base::func2" << endl; }
void func5() { cout << "Base::func5" << endl; }
protected:
int a = 1;
};
class Derive : public Base
{
public:// 重写基类的func1virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func1" << endl; }void func4() { cout << "Derive::func4" << endl; }
protected:int b = 2;
};
int main()
{Base b;cout << sizeof(b) << endl;Derive d;return 0;
}

 "Keep coding, stay curious! 🚀"

 

 

http://www.xdnf.cn/news/996409.html

相关文章:

  • 基于llamafactory微调千问大模型(实战)
  • 延时神经网络 vs CNN vs RNN:时空建模的三种武器对比
  • 已连接(connected)UDP和未连接(unconnected)UDP的区别
  • 27-Oracle 23 ai Automatic Rollback Quarantine(事务精准隔离)
  • 16、企业预算管理(Budget)全流程解析:从预算编制到预算控制
  • Python Docker 镜像构建完整指南:从基础到优化
  • 全面理解BUUCTF-rip1
  • 苍穹外卖Day11代码解析以及深入思考
  • node.js连接mysql写接口(一)
  • Go语言高并发爬虫程序源码
  • Objective-c protocol 练习
  • 华为云河图:数字孪生技术驱动城市管理智能化变革
  • android 之 CALL
  • 【Create my OS】从零编写一个操作系统
  • 解决鸿蒙开发修改实体类某个字段,页面不刷新的问题
  • Android自动化AirScript
  • Rust 学习笔记:关于通过异步实现并发的练习题
  • nginx配置gzip压缩
  • 《深入理解Apache Dubbo 与实战》笔记
  • SpreadJS 中 HTML Canvas 的性能优势深度解析
  • Flask 中结合 Jinja2 模板引擎返回渲染后的 HTML
  • 关于 /proc/net/tcp 与 /proc/$pid/net/tcp 的关系分析
  • python中的循环结构
  • 多参表达式Hive UDF
  • 如何确定某个路由器的路由表?(计算机网络)
  • Flink读取Kafka写入Paimon
  • C++11中char16_t和char32_t的入门到精通
  • 黑马点评面试话术
  • uniapp 时钟
  • 电动汽车驱动模式扭矩控制设计方法