C++類成員函數


c++的兩大特色是多態和模板。其中多態是通過繼承和虛函數來實現的,其中虛函數是通過每個對象里面的虛表來實現的。如果這個對象的類有虛函數,那么這個類就有一張虛表,存的是每個虛函數的入口地址,而這個類的每個對象,都會有一個4字節的指針,指向這張虛表,這個就是虛指針。

上面一段話很多人都知道,但是如果問普通成員函數,編譯器是怎么找到它的入口地址的呢?也就是說,怎么進行調用?為什么A類一個foo函數和B類一個foo函數,A類的對象.foo就一定是調用A的foo?有人會說運行時類型識別RTTI。假如識別出A類的對象,那么A類的對象a,和A類的對象b,分別調用foo處理成員變量的時候,為什么不會a調用foo處理的是b的數據?這個問題只要問深幾層,RTTI的說法就不攻自破。其實面向對象語言都是用反射,也就是對象把自己的地址作為參數,傳入成員函數的this指針,通過this指針來確定數據的。

還記得函數重載的實現機制吧,在c++里面是通過將函數名和函數的參數列表結合來區分不同的函數,同名但不同參數列表的函數,是不同的函數。例如void foo(int,int)在c里面編譯器是用_foo來識別,在c++里面是用_foo_int_int來識別。

那么A類的foo函數和B類的foo函數,如果參數列表一致,怎么區分它們呢?用A類的一個對象調用成員函數,這個成員函數用到成員變量,怎么知道是在用這個對象的數據呢?這個解釋,就是this指針。A類的foo(int,int)函數,已經被編譯器編譯成_foo_A*_int_int,也就是說隱含已經加入了函數所屬類的信息,調用A類對象a.foo的時候,已經在調用foo(const A * this,int,int)這個函數。這也解釋了,為什么在成員函數內部,使用成員變量,有時候(並不是全部情況下都如此)前面加this和不加this沒有區別,編譯器已經把foo里面用到的成員變量,用this指針指向了。

如果你在全局中,這樣定義這樣一個函數foo(const A * this,int,int)去模仿成員函數的調用,將不能編譯,因為this是關鍵字,this指針已經成為了c++的機制,在類成員函數用this才做形參(foo(int this)或者foo(float this)),也是非法的。這樣試圖和編譯器生成的函數的第一個參數this來一個重定義。

舉個例子,下面一段代碼:

 1 class A
 2 {
 3 public:
 4     void foo(){ cout << "A foo" << endl; }
 5 };
 6 class B
 7 {
 8 public:
 9     void foo(){ cout << "B foo" << endl; }
10 };

oo將編譯成:

1 void foo(const A* this)( cout << "A foo" << endl; }
2 void foo(const B* this)( cout << "B foo" << endl; }

調用a.foo(),編譯器將轉換成foo(&a)

有趣的是,A* pa = NULL; pa->foo();也沒有異常退出,因為沒有通過this引用任何成員變量,這個時候不過this指針為NULL而已。

靜態成員函數

上面說的只是面向對象的非靜態成員函數,如果說到類里面的靜態成員函數,解釋又是另外一個,請看下文。

1、靜態數據成員   
  特點:   
    A、內存分配:在程序的全局數據區分配。   
    B、初始化和定義:   
     a、靜態數據成員定義時要分配空間,所以不能在類聲明中定義。   
     b、為了避免在多個使用該類的源文件中,對其重復定義,所在,不能在類的頭文件中   
      定義。     
     c、靜態數據成員因為程序一開始運行就必需存在,所以其初始化的最佳位置在類的內部實現。   
    C、特點   
     a、對相於   public,protected,private   關鍵字的影響它和普通數據成員一樣,     
     b、因為其空間在全局數據區分配,屬於所有本類的對象共享,所以,它不屬於特定的類對象,在沒產生類對象時其作用域就可見,即在沒有產生類的實例時,我們就可以操作它。  
    D、訪問形式   
     a、   類對象名.靜態數據成員名   
     b、   類類型名::   靜態數據成員名   
    E、靜態數據成員,主要用在類的所有實例都擁有的屬性上。比如,對於一個存款類,帳號相對   於每個實例都是不同的,但每個實例的利息是相同的。所以,應該把利息設為存款類的靜態數據成員。這有兩個好處,第一,不管定義多少個存款類對象,利息數據成員都共享分配在全局區的內存,所以節省存貯空間。第二,一旦利息需要改變時,只要改變一次,則所有存款類對象的利息全改變過來了,因為它們實際上是共用一個東西。   
    
  2、靜態成員函數   
  特點:   
    A、靜態成員函數與類相聯系,不與類的對象相聯系。   
    B、靜態成員函數不能訪問非靜態數據成員。原因很簡單,非靜態數據成員屬於特定的類實例。   
  作用:   
    主要用於對靜態數據成員的操作。   
  調用形式:   
    A、類對象名.靜態成員函數名()   
    B、類類型名::   靜態成員函數名()

 

上面是一段精辟的分析,但是他沒有說道編譯器是如何實現這個調用的。下面一段代碼,將解釋這個過程:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class A
 5 {
 6 public:
 7     static int count;
 8     void foo(){ cout << "A foo" << endl; }    // 如果這樣聲明和定義一個成員函數,將直接產生一個 foo(A& this) 類型的函數
 9     static void goo(){ cout <</* this << */"A goo"<< endl; }    // 靜態函數沒有 this 指針
10     void too(){ cout << typeid(*this).name() << endl; }
11 };
12 
13 int A::count = 0;
14 
15 class B : public A
16 {
17 public:
18     // 如果靜態函數只能通過域運算符來調用的話,那class在靜態意義下就成了命名域的概念了
19     // 如果沒有下面這個函數,A類的goo函數將會繼承下來,說明作為類的命名空間,也可以繼承
20     static void goo(){ cout << "B goo" << endl;}        // 一個問題,靜態的成員函數,是怎么區分開的呢?
21 };
22 
23 int main()
24 {
25     A a , *pa;
26     pa->goo();        // 靜態也跟普通函數一樣,沒有多態效果
27     a.goo();        // 是否是直接翻譯成 A::goo() 竟然說我沒引用過 a !- -
28     // a.foo();        // this 是關鍵字,不能拿來作為一個全局函數的參數,在轉成 foo(&a) 的時候,一定是調用 foo(A& this) 這個函數
29     // A::foo();        // 這個 foo 沒有帶參數,只能調用靜態的,靜態的就直接編譯成類似全局函數的不帶 this 參數的類型
30     A::goo();        // 這樣調用是正確的,這說明它沒有 this 指針作為形參
31     system("pause");
32     return 0;
33 }

編譯運行,將產生一個警告,說對象a和指針pa從來沒引用過!通過對象調用靜態函數,已經通過類型識別,被編譯器替換成A::goo(),這個是由編譯器做的,所以替換之后a就只定義了,但是沒用引用過。換句話,static的成員函數,只能通過域運算符來調用,無論你是用對象調用還是用指針調用。

static的成員變量,也是如此,只能通過翻譯成域運算符來調用。這樣兩者結合在一起,說明了“類其實除了可以定義變量,還有一個重要的作用就是它是個命名域,相當於std::cout這樣。而且,這個命名域,還能繼承下來。”

還記得stl里面的迭代器嗎?迭代器的類,就是為了不污染全局空間,把迭代器類聲明在某個stl容器類里面。這個類里面的類,只能通過容器和域運算符才可見,有趣的是,這個類中類,也可以繼承。

#include <iostream>
using namespace std;

class A
{
public:
    class C{};                                // 類中類
};

class B : public A
{
};

int main()
{
    B::C c;            // 除了父類,子類也可見
    return 0;
}

 


這里由於某個問題找到了這邊博文,感覺不錯,轉自:http://blog.csdn.net/yuanyirui/article/details/4594805


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM