首先来看一道编程题:写出输出的结果
#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
这里主要涉及两个方面:1)对象调用成员函数的时候会将对象与函数绑定;****2)对象访问成员变量是根据成员变量距离对象的偏移量来访问的,而不是根据成员名来访问。(所谓偏移量就是告诉你一个特定的成员位置距离对象起点有多少个字节)
接着来分析为什么输出结果是1.
a.内存中实例化一个A的对象a。
b.将a对象的地址强制转化为B类的地址。
c.pb为B类的指针,理所当然调用的是B类的fun函数。
d.当调用fun函数的时候,调用对象与该函数进行绑定,即fun函数中隐含的形参this指针初始化为调用A类对象的地址。
e.按照道理,应该直接打印m_c的值。这里要注意,对象在访问类成员的时候,编译器并没有存储对象各个成员的实际地址,而是存储了其相对对象首地址的偏移量。
f.由于类B只有一个成员m_c,在编译阶段,编译器就记录了m_c相对于B类的偏移量为0,故访问m_c的时候,便是访问当前对象this+偏移量
g.注意,这里的this指针指向的是A类对象的首地址,在A类中偏移量 为0的是m_a。故打印m_a的值。
二.关于成员指针
1)成员指针只是记录一个成员的偏移量,而非地址,因为类中没有地址。
2)选择一个类的成员只是意味着在类中偏移,只有把这个偏移和具体对象的首地址结合,才能得到实际地址。
3)成员指针并不指向一个具体的内存位置,它指向的是一个类的特定成员,而不是指向一个特定对象的特定成员,最直接的理解是将其理解为一个偏移量。这个偏移量适用于某一类A的任何对象,换言之,如果一个A类对象的成员a距离起点的偏移量是4,那么任何其他A类对象中,a的偏移都是4字节。
总结:
1.类对象访问成员时候,是根绝成员函数相对于类对象的地址偏移来访问的。
2.类成员指针,可以理解为指向类数据成员的一个偏移量,而非地址。