C++多重继承的指针问题
下面说说C++多重继承中关于指针的一些问题。
指针指向问题 先看下面的程序:
class Base1
{
public:
virtual void fun1() {cout << "Base1::fun1" << endl;};
};
class Base2
{
public:
virtual void fun2() {cout << "Base2::fun1" << endl;};
};
class Derive : public Base1, public Base2
{
public:
virtual void fun1 () {cout << "Derive::fun1" << endl;}
virtual void fun2 () {cout << "Derive::fun2" << endl;}
};
int main()
{
Derive oD;
Base1 *pB1 = (Base1*)(&oD);
Base2 *pB2 = (Base2*)(&oD);
cout << "&oD=" << &oD << 'n';
cout << "pB1=" << pB1 << 'n';
cout << "pB2=" << pB2 << 'n';
if (&oD == pB1) cout << "&oD == pB1" << 'n';
if (&oD == pB2) cout << "&oD == pB2" << 'n';
}
我电脑上的运行结果:
首先,可以看到&oD和pB1指针指向相同的存储地址。为什么? 这是因为当我们new一个Derive类的时候,计算机给Derive类分配的空间可以分为三部分:首先是类Base1的部分,然后是Base2的部分,然后是Derive中除去Base和Base2剩余部分,如下图。
Base1 |
---|
Base2 |
Derive |
所以&oD肯定保存的是整体的首地址,而pB1指向的是Base1的首地址,恰好也是整体的首地址,所以有&oD和pB1的值刚好相等。pB2则指向的是Base2的首地址。 可是后面为什么会有&oD == pB2呢?这是因为当编译器发现一个指向派生类的指针和指向其某个基类的指针进行==运算时,会自动将指针做隐式类型提升已屏蔽多重继承带来的指针差异。因为两个指针做比较,目的通常是判断两个指针是否指向了同一个内存对象实例,在上面的场景中,&oD和pB2虽然指针值不等,但是他们确确实实都指向了同一个内存对象(即new Derive产生的内存对象)。
指针类型转换问题 还是使用上面的类,看主函数:
int main(){
Derive oD;
cout << "&oD=" << &oD << 'n';
Base1 *pB1 = &oD;
cout << "pB1=" << pB1 << 'n';
pB1->fun1();
cout << endl;
Base2 *pB2 = (Base2*)(pB1); // 指针强行转换,没有偏移
cout << "pB2=" << pB2 << 'n';
pB2->fun2();
cout << endl;
pB2 = dynamic_cast<Base2*>(pB1); // 指针动态转换,dynamic_cast帮你偏移
cout << "pB2=" << pB2 << 'n';
pB2->fun2();
return 0;
}
猜猜执行结果:
是不是很意外,为什么pB2->fun2()的结果是Derive::fun1。这里我们看到的是使用强制类型转换是不能把Base1类型的指针转成Base2类型的指针的,必须使用dynamic_cast的形式进制转换才奏效。
下面我们探索下为什么输出的是Derive::fun1。 我们修改Base1的定义:
class Base1
{
public:
virtual void fun3() {cout << "Base1::fun3" << endl;};
virtual void fun1() {cout << "Base1::fun1" << endl;};
};
给添加一个函数fun3,然后再次执行上面的main函数,结果如下:
我们可以发现强制转换不会成功,也不会报错,你调用Base2的fun2函数,因为强制转换不成功,所以指针仍然指向Base1,而Base1中没有fun2,所以就会自动调用声明的函数中的第一个函数。(不知道为什么会这样设计!)
上面强制将Base1转为Base2不会报错,但是不能运行处正确结果。而我们强制将Base2转为Base1呢?
int main()
{
Derive *pD = new Derive();
Base2 *pB2 = pD;
pB2->fun2();
Base1 *pB1 = (Base1*)(pB2);
pB1->fun1();
return 0;
}
这样程序执行到第6行的时候会直接奔溃。 所以:
1. C++多重继承需要慎用
2. 类型转换尽量采用c++内置的类型转换函数,而不要强行转换。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 海康IPCamera结合OpenCV图像处理的一般步骤
- 聊聊java中的哪些Map:(十)各种map的总结
- SwiftUI:辅助功能——项目优化示例
- Visualizing the impact of ordered vs. random index insertion in InnoDB (16 顺序插入和随机插入索引的影响可视化分析)
- 一点思考|工作十几年了,竟从未用过do-while!
- springboot 默认日志配置源码
- logback 通过javaconfig实现配置
- springboot通过javaconfig实现logback配置
- 干货来了,vue 3.0 自定义指令变化
- XXE注入漏洞
- vue + typescript 类组件教程
- 图像处理笔记(3)----OpenCV图像算术运算
- V - Distpicker 是一个简单易用的地区选择器
- [漫谈] 状态
- 变量覆盖