一个问题引发的对类成员指针的思考

原题转自《程序员面试宝典》

#include <stdio.h>
 
class A
{
public:
     A() {m_a = 1; m_b = 2;}
     ~A() {}
     void fun() {printf("%d %d", m_a, m_b);}
private:
     int m_a;
     int m_b;
};
 
class B
{
public:
     B() {m_c = 3;}
     ~B() {}
     void fun() {printf("%d", m_c);}
private:
     int m_c;
};
 
void main()
{
     A a;
     B *pb = (B*)(&a);
     pb->fun();
}


上面程序打印的结果为1;

在面对类中数据成员的时候,类找寻数据的时候靠的是一种叫做成员指针的家伙,和普通的指针一样,指针的值为相对程序开始的物理地址的相对地址,也就是我们平常所说的相对地址偏移量,在pb->fun的时候,fun函数在打印m_c的时候,是直接从原本存放着a的空间开始,向下偏移4个字节,然后进行处理的,所以打印出来的值是A的默认构造值1;

既然数据是通过偏移量来使用的,那么思考函数是否也是通过偏移量来决定的,考虑一下代码

#include <iostream>
using namespace std;

class A
{
public:
    A() : m_a(1),m_b(2){}
    void fun()
    {
        cout<<"呵呵,上当了吧"<<endl;
    }
    void sun()
    {
        cout<<m_a<<" "<<m_b<<endl;
    }
    void vim()
    {
        cout<<"上当了吧"<<endl;
    }
private:
    int m_a;
    int m_b;
};

class B
{
public:
    B() : m_c(3){}
    void sun()
    {
        cout<<m_c<<endl;
    }
    void fun()
    {
        cout<<"呵呵,上当了吧"<<endl;
    }
private:
    int m_c;
};

int main()
{
    A a;
    B *pb = (B*)(&a);
    pb->sun();
    return 0;
}


打印的结果还是1,这就说明类在确定引用的函数的时候不是通过偏移量来决定的,这很显然,我们在VC上查看汇编语言可以知道,C++确定调用的函数是通过函数原型的机制来确定的,如图,我们看vc6.0里的汇编代码



从这一层我们可以看到,简单的看b中的fun的标志是b::fun

然后我们再一层的深入,可以看到,fun的函数原型

由此我们可以得出,类中确定函数调用选择的时候是通过辨别函数原型的方式进行的。


然后,我们考虑类成员指针的其他性质

在《C++编程思想》中第267页有一个关于类成员指针的描述

书中给出了类成员指针声明和定义的方法,包括基本的哦使用,以下是本人修改后的代码,与书中略有不同

class Data
{
public:
    int a;
    double c;
    Data() : a(5),c(2){}
    void fun()
    {
        cout<<"调用成功"<<endl;
    }
    void sun()
    {
        cout<<"sun"<<endl;
    }
};

class Dato
{};

int main()
{
    int Data::*p1 = &Data::a;
    double Data::*p2 = &Data::c;
    void (Data::*p3)() = &Data::fun;
    void (Data::*p4)() = &Data::sun;

    return 0;
}

基本的使用,请读者参看相关书目,我在这里要说的一点是一个,让我很不解的现象

就是我在定义了上述的指针后,又再其后添加了以下代码

    cout<<p1<<endl;
    cout<<p2<<endl;
    cout<<p3<<endl;
    cout<<p4<<endl;

我们知道,使用cout打印指针会打印出指针的相对地址,但是在这里,很奇怪的是,这里打印的结果全部是1。感觉完全无法理解了,话说指针不是应该是偏移量吗,怎么会是1呢。关于这个问题,我查阅了一些资料后,找到了如下文章

成员在类中的偏移量 & 类成员指针

http://www.ahathinking.com/archives/98.html


在这个文章里,作者给出的解释简单来说,是因为iostream没有与成员指针匹配的函数重载,所以,全部都打印为1.

对于这个问题,我特意添加头文件stdio.h,然后再在后面添加如下代码段,

    printf("%d\n",p1);
    printf("%d\n",p2);
    printf("%d\n",p3);
    printf("%d\n",p4);


最终打印的结果为

有趣的是,我在全局区和代码区随意添加变量,打印出来的结果不变,但是当我在全局区,随意定义一个函数之后,发现p3,p4的地址发生了改变。并且随意添加的函数的地址和p3,p4的地址接近,故我下判断,认为编译器出于某种原因(可能是防止错误的指针导致难以解除的错误),把数据所在的区域和函数原型所在的区域分离开来,但具体类的成员函数和普通函数有没有特意区分还不好判断。

由此我

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值