前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++多重继承的指针问题

C++多重继承的指针问题

作者头像
卡尔曼和玻尔兹曼谁曼
发布2019-01-22 11:40:44
8750
发布2019-01-22 11:40:44
举报
文章被收录于专栏:给永远比拿愉快

下面说说C++多重继承中关于指针的一些问题。

指针指向问题 先看下面的程序:

代码语言:javascript
复制
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产生的内存对象)。


指针类型转换问题 还是使用上面的类,看主函数:

代码语言:javascript
复制
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的定义:

代码语言:javascript
复制
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呢?

代码语言:javascript
复制
int main()
{
    Derive *pD = new Derive();
    Base2 *pB2 = pD;
    pB2->fun2();
    Base1 *pB1 = (Base1*)(pB2);
    pB1->fun1();
    return 0;
}

这样程序执行到第6行的时候会直接奔溃。 所以:

代码语言:javascript
复制
1. C++多重继承需要慎用
2. 类型转换尽量采用c++内置的类型转换函数,而不要强行转换。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015年05月17日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档