先问一个问题,在C++里,成员函数里的this指针和调用此函数的对象地址总是一样的吗?如果你的回答是:不一定。那么至少你是个老手吧,下面的内容你就不用看了;如果你的回答是:是啊,那么强烈建议你看看下面的内容。
非静态成员函数,无论是不是虚函数,都隐藏了一个this指针参数。这个参数的目的就是给函数提供一个基地址,以便于函数体内能找到对象的成员变量。那非静态成员函数是如何根据this指针找到成员变量的呢?直接看例子吧
1没有虚表的情况
- #include<iostream>
-
#include<stdio.h>
-
usingnamespacestd;
-
classA
- {
-
public:
-
intx;
-
inty;
-
public:
-
voidF1()
- {
-
this->x=1;
-
this->y=2;
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
- };
-
intmain(intargc,char**argv)
- {
- Aa;
-
cout<<"a对象的地址是:"<<&a<<endl;
-
cout<<"a对象的大小是:"<<sizeof(A)<<endl;
-
cout<<"成员a.x的地址是:"<<&a.x<<endl;
-
cout<<"成员a.x的偏移是:"<<&A::x<<endl;
- a.F1();
- cin>>argc;
-
return0;
- }
那么函数F1的实现伪代码为:
*(this+&A::x-1) = 1;
*(this+&A::y-1) = 2;
其中&A::x是成员x的偏移+1,&A::y是成员y的偏移+1,这可是C++基本语法的知识,希望你知道,呵呵。可这些偏移量是相对应那里的偏移呢,是对象地址吗,答案是NO。是相对于第一个成员变量的偏移,这对于有些对象也许没有差别,但是对于有虚表的类的对象,就有差别了。这些偏移在编译期间就是确定了的。对于本例在VC++2010下&A::x,值为1, &A::y,值为5。为什么不是0,4,请看《Inside The C++ Object Model》。
所以,对于找到成员变量,需要进一步确定的只有this的值。程序运行结果如下:
可见此例中,对象的地址与this指针的地址相同,内存图如下所示。
2有一个虚表的情况
- #include<iostream>
-
#include<stdio.h>
-
usingnamespacestd;
-
classA
- {
-
public:
-
intx;
-
inty;
-
public:
-
virtualvoidF1()
- {
-
this->x=1;
-
this->y=2;
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
- };
-
intmain(intargc,char**argv)
- {
-
A*p=newA();
-
cout<<"a对象的地址是:"<<p<<endl;
-
cout<<"a对象的大小是:"<<sizeof(A)<<endl;
-
cout<<"成员a.x的地址是:"<<&p->x<<endl;
-
cout<<"成员a.x的偏移是:"<<&A::x<<endl;
- p->F1();
- cin>>argc;
-
return0;
- }
此时函数F1的实现伪代码为:
*(this+4+&A::x-1) = 1; //+4是因为存在虚表指针
*(this+4+&A::y-1) = 2; //+4是因为存在虚表指针
程序运行结果如下:
内存布局如下:
结论:this的值和对象地址相同,成员变量偏移量不因虚表指针存在而改变。带虚表的类的成员函数对成员变量的寻址方式不同,增加一个+4。
3单继承的情况
- #include<iostream>
-
#include<stdio.h>
-
usingnamespacestd;
-
classA
- {
-
public:
-
intx;
-
inty;
-
public:
-
voidF1()
- {
-
this->x=1;
-
this->y=2;
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
- };
-
classB:publicA
- {
-
public:
-
intz;
-
public:
-
virtualvoidF2()
- {
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
- };
-
intmain(intargc,char**argv)
- {
-
B*pb=newB();
-
cout<<"对象的地址为:"<<std::hex<<std::showbase<<pb<<endl;
- pb->F1();
- pb->F2();
- cin>>argc;
-
return0;
- }
运行结果:
内存布局:
结论:this指针的值受两个因素确定,一是对象的地址,二是定义成员函数的类。This指向的是对象内,定义该方法的类得subobject。
4 多继承的情况
先看A没有虚函数,B有虚函数的情况
- #include<iostream>
-
#include<stdio.h>
-
usingnamespacestd;
-
classA
- {
-
public:
-
intx;
-
inty;
-
public:
-
voidF1()
- {
-
this->x=1;
-
this->y=2;
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
- };
-
classB
- {
-
public:
-
intz;
-
public:
-
virtualvoidF2()
- {
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
- };
-
classC:publicA,publicB
- {
-
public:
-
inta;
-
public:
-
virtualvoidF2()
- {
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
-
voidF3()
- {
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
- };
-
intmain(intargc,char**argv)
- {
-
C*pc=newC();
-
cout<<"对象的大小为:"<<sizeof(C)<<endl;
-
cout<<"对象的地址为:"<<std::hex<<std::showbase<<pc<<endl;
- pc->F1();
- pc->F2();
- pc->F3();
- cin>>argc;
-
return0;
- }
结果:
内存布局:
再看,如果A,B都有虚函数的情况。
代码:
- #include<iostream>
-
#include<stdio.h>
-
usingnamespacestd;
-
classA
- {
-
public:
-
intx;
-
inty;
-
public:
-
virtualvoidF1()
- {
-
this->x=1;
-
this->y=2;
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
- };
-
classB
- {
-
public:
-
intz;
-
public:
-
virtualvoidF2()
- {
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
- };
-
classC:publicA,publicB
- {
-
public:
-
inta;
-
public:
-
virtualvoidF2()
- {
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
-
voidF3()
- {
-
cout<<"this指针得值是:"<<std::hex<<std::showbase<<this<<endl;
- }
- };
-
intmain(intargc,char**argv)
- {
-
C*pc=newC();
-
cout<<"对象的大小为:"<<sizeof(C)<<endl;
-
cout<<"对象的地址为:"<<std::hex<<std::showbase<<pc<<endl;
-
cout<<"x的地址"<<&pc->x<<endl;
-
cout<<"z的地址"<<&pc->z<<endl;
- pc->F1();
- pc->F2();
- pc->B::F2();
- pc->F3();
- cin>>argc;
-
return0;
- }
结果:
内存布局:
结论:再一次验证了this指针的值的确定方法,this始终要保证指向定义了该成员函数的类得subobject。因为C++保证base class subobject与base class object完全对应,从而保证了成员函数能根据成员变量在定义了该变量的类中的偏移寻址。
分享到:
相关推荐
涉及各种情况下C++对象的sizeof大小,包括单一类对象,继承,重复继承 多继承 单一虚继承 等各种情况下的对象大小。对C++对象内存布局有清楚了解。
看了这个内存布局图详解之后,对于C++的了解更加深刻了,之前不懂得一头雾水的东西全都清楚了。
介绍C++对象在内存中是怎样分布的,有助于深层学习C++。
1* 类如何布局? 2* 成员变量如何访问? 3* 成员函数如何访问? 4* 所谓的“调整块”(adjuster thunk)是怎么回事? 5* 使用如下机制时,开销如何: * 单继承、多重继承、虚继承 * 虚函数调用 * 强制转换...
1、问:程序运行需要哪些空间?答:栈区、堆区、常量区等。 2、问:动态空间如何使用?答:通过指针 3、问:常量一定是在常量区吗?答:不一定 4、问:宏定义的常量是怎么回事?答:宏代换 5、问:字符串常量的存储...
在runtime.h里有个计算程序运行时间的代码,精确到毫秒
程序运行时内存不能读的一些解决办法
C++对象内存布局[归类].pdf
C++语言编程中关于字符串编程。获取程序运行目录i,源码
1)有成员变量的情况。 2)有重复继承的情况。 3)有虚拟继承的情况。 4)有钻石型虚拟继承的情况。
这是一个以c++为基础编写的一个测试内存的程序,仅用于学习交流
如何写一个C++程序,执行A.exe,计算A.exe的运行时间和内存。
6.如何使用内存映射文件实现多个程序间互斥运行?(Visual C++编程 源代码)6.如何使用内存映射文件实现多个程序间互斥运行?(Visual C++编程 源代码)6.如何使用内存映射文件实现多个程序间互斥运行?(Visual C++...
c/c++ 实现程序运行时间精确测量,本程序测得时间十分准确,精度为ms
C++高手必过内存管理关,探讨C++内存回收,C++内存泄漏及其检测工具
C++学习笔记:关于C++类与动态内存分配中布局new操作符
6.如何将应用程序窗口居中显示?(Visual C++编程 源代码)6.如何将应用程序窗口居中显示?(Visual C++编程 源代码)6.如何将应用程序窗口居中显示?(Visual C++编程 源代码)6.如何将应用程序窗口居中显示?...
c++继承中的内存布局 - 开源中国社区