C++程序運行時內存佈局之--this究竟是什麼?

先問一個問題,在C++裏,成員函數裏的this指針和調用此函數的對象地址老是同樣的嗎?若是你的回答是:不必定。那麼至少你是個老手吧,下面的內容你就不用看了;若是你的回答是:是啊,那麼強烈建議你看看下面的內容。ios

 

非靜態成員函數,不管是否是虛函數,都隱藏了一個this指針參數。這個參數的目的就是給函數提供一個基地址,以便於函數體內能找到對象的成員變量。那非靜態成員函數是如何根據this指針找到成員變量的呢?直接看例子吧ide

 

1沒有虛表的狀況函數

  1. #include <iostream>  
  2. #include <stdio.h>  
  3.   
  4. using namespace std;  
  5.   
  6. class A  
  7. {  
  8. public:  
  9.     int x;  
  10.     int y;  
  11. public:  
  12.     void F1()  
  13.     {  
  14.         this->x = 1;  
  15.         this->y = 2;  
  16.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  17.     }  
  18. };  
  19.   
  20.   
  21.   
  22. int main(int argc, char** argv)  
  23. {  
  24.     A a;  
  25.     cout<<"a對象的地址是:"<<&a<<endl;  
  26.     cout<<"a對象的大小是:"<<sizeof(A)<<endl;  
  27.     cout<<"成員a.x的地址是: "<<&a.x<<endl;  
  28.     cout<<"成員a.x的偏移是:"<<&A::x<<endl;  
  29.     a.F1();  
  30.     cin>>argc;  
  31.     return 0;  
  32. }  

那麼函數F1的實現僞代碼爲:佈局

 

*(this+&A::x-1) = 1;this

*(this+&A::y-1) = 2;spa

 

其中&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》。.net

因此,對於找到成員變量,須要進一步肯定的只有this的值。程序運行結果以下:指針

可見此例中,對象的地址與this指針的地址相同,內存圖以下所示。orm

2有一個虛表的狀況對象

  1. #include <iostream>  
  2. #include <stdio.h>  
  3.   
  4. using namespace std;  
  5.   
  6. class A  
  7. {  
  8. public:  
  9.     int x;  
  10.     int y;  
  11. public:  
  12.     virtual void F1()  
  13.     {  
  14.         this->x = 1;  
  15.         this->y = 2;  
  16.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  17.     }  
  18. };  
  19.   
  20.   
  21.   
  22. int main(int argc, char** argv)  
  23. {  
  24.     A* p = new A();  
  25.     cout<<"a對象的地址是:"<<p<<endl;  
  26.     cout<<"a對象的大小是:"<<sizeof(A)<<endl;  
  27.     cout<<"成員a.x的地址是: "<<&p->x<<endl;  
  28.     cout<<"成員a.x的偏移是:"<<&A::x<<endl;  
  29.     p->F1();  
  30.     cin>>argc;  
  31.     return 0;  
  32. }  


 

此時函數F1的實現僞代碼爲:

 

*(this+4+&A::x-1) = 1;  //+4是由於存在虛表指針

*(this+4+&A::y-1) = 2;  //+4是由於存在虛表指針

 

程序運行結果以下:

內存佈局以下:

結論:this的值和對象地址相同,成員變量偏移量不因虛表指針存在而改變。帶虛表的類的成員函數對成員變量的尋址方式不一樣,增長一個+4。

3單繼承的狀況

  1. #include <iostream>  
  2. #include <stdio.h>  
  3.   
  4. using namespace std;  
  5.   
  6. class A  
  7. {  
  8. public:  
  9.     int x;  
  10.     int y;  
  11. public:  
  12.     void F1()  
  13.     {  
  14.         this->x = 1;  
  15.         this->y = 2;  
  16.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  17.     }  
  18. };  
  19.   
  20. class B : public A  
  21. {  
  22. public:  
  23.     int z;  
  24. public:  
  25.     virtual void F2()  
  26.     {  
  27.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  28.     }  
  29. };  
  30.   
  31. int main(int argc, char** argv)  
  32. {  
  33.     B* pb = new B();  
  34.     cout<<"對象的地址爲:"<<std::hex<<std::showbase<<pb<<endl;  
  35.     pb->F1();  
  36.     pb->F2();  
  37.   
  38.     cin>>argc;  
  39.     return 0;  
  40. }  


 

運行結果:

內存佈局:

結論:this指針的值受兩個因素肯定,一是對象的地址,二是定義成員函數的類。This指向的是對象內,定義該方法的類得subobject。

4 多繼承的狀況

先看A沒有虛函數,B有虛函數的狀況

  1. #include <iostream>  
  2. #include <stdio.h>  
  3.   
  4. using namespace std;  
  5.   
  6. class A  
  7. {  
  8. public:  
  9.     int x;  
  10.     int y;  
  11. public:  
  12.     void F1()  
  13.     {  
  14.         this->x = 1;  
  15.         this->y = 2;  
  16.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  17.     }  
  18. };  
  19.   
  20. class B  
  21. {  
  22. public:  
  23.     int z;  
  24. public:  
  25.     virtual void F2()  
  26.     {  
  27.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  28.     }  
  29. };  
  30.   
  31. class C : public A, public B  
  32. {  
  33. public:  
  34.     int a;  
  35. public:  
  36.     virtual void F2()  
  37.     {  
  38.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  39.     }  
  40.     void F3()  
  41.     {  
  42.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  43.     }  
  44. };  
  45. int main(int argc, char** argv)  
  46. {  
  47.     C* pc = new C();  
  48.     cout<<"對象的大小爲:"<<sizeof(C)<<endl;  
  49.     cout<<"對象的地址爲:"<<std::hex<<std::showbase<<pc<<endl;  
  50.     pc->F1();  
  51.     pc->F2();  
  52.     pc->F3();  
  53.   
  54.     cin>>argc;  
  55.     return 0;  
  56. }  


結果:

內存佈局:

再看,若是A,B都有虛函數的狀況。

代碼:

  1. #include <iostream>  
  2. #include <stdio.h>  
  3.   
  4. using namespace std;  
  5.   
  6. class A  
  7. {  
  8. public:  
  9.     int x;  
  10.     int y;  
  11. public:  
  12.     virtual void F1()  
  13.     {  
  14.         this->x = 1;  
  15.         this->y = 2;  
  16.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  17.     }  
  18. };  
  19.   
  20. class B  
  21. {  
  22. public:  
  23.     int z;  
  24. public:  
  25.     virtual void F2()  
  26.     {  
  27.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  28.     }  
  29. };  
  30.   
  31. class C : public A, public B  
  32. {  
  33. public:  
  34.     int a;  
  35. public:  
  36.     virtual void F2()  
  37.     {  
  38.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  39.     }  
  40.     void F3()  
  41.     {  
  42.         cout<<"this指針得值是:"<<std::hex<<std::showbase<<this<<endl;  
  43.     }  
  44. };  
  45. int main(int argc, char** argv)  
  46. {  
  47.     C* pc = new C();  
  48.     cout<<"對象的大小爲:"<<sizeof(C)<<endl;  
  49.     cout<<"對象的地址爲:"<<std::hex<<std::showbase<<pc<<endl;  
  50.     cout<<"x的地址"<<&pc->x<<endl;  
  51.     cout<<"z的地址"<<&pc->z<<endl;  
  52.     pc->F1();  
  53.     pc->F2();  
  54.     pc->B::F2();  
  55.     pc->F3();  
  56.   
  57.     cin>>argc;  
  58.     return 0;  
  59. }  


結果:

內存佈局:

結論:再一次驗證了this指針的值的肯定方法,this始終要保證指向定義了該成員函數的類得subobject。由於C++保證base class subobject與base class object徹底對應,從而保證了成員函數能根據成員變量在定義了該變量的類中的偏移尋址。

相關文章
相關標籤/搜索