原题转自《程序员面试宝典》
#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的地址接近,故我下判断,认为编译器出于某种原因(可能是防止错误的指针导致难以解除的错误),把数据所在的区域和函数原型所在的区域分离开来,但具体类的成员函数和普通函数有没有特意区分还不好判断。
由此我