c 類成員函式

2021-05-06 15:49:15 字數 2634 閱讀 3038

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來乙個重定義。

舉個例子,下面一段**:

foo將編譯成:

呼叫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、類型別名::   靜態成員函式名()

上面是一段精闢的分析,但是他沒有說道編譯器是如何實現這個呼叫的。下面一段**,將解釋這個過程:

編譯執行,將產生乙個警告,說物件a和指標pa從來沒引用過!通過物件呼叫靜態函式,已經通過型別識別,被編譯器替換成a::goo(),這個是由編譯器做的,所以替換之後a就只定義了,但是沒用引用過。換句話,static的成員函式,只能通過域運算子來呼叫,無論你是用物件呼叫還是用指標呼叫。

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

還記得stl裡面的迭代器嗎?迭代器的類,就是為了不汙染全域性空間,把迭代器類宣告在某個stl容器類裡面。這個類裡面的類,只能通過容器和域運算子才可見,有趣的是,這個類中類,也可以繼承。

c 類成員函式

類成員函式是類的乙個成員,它可以操作類的任意物件,可以訪問物件中的所有成員。定義類box,使用成員函式來訪問類的成員,而不是直接訪問這些類的成員 class box class box double box getvolume void 在這裡,需要強調一點,在 運算子之前必須使用類名。呼叫成員函式...

c 類成員函式

靜態型別 表示式在編譯時型別是已知的,它是變數宣告時和表示式生成的型別 動態型別 表達是表示式在記憶體中的型別 如果表示式不是指標或引用,則它的靜態型別和動態型別永遠一樣。person的靜態型別是person,它的動態型別可能是student,teacher.string job person.jo...

C 類成員函式的儲存方式 C 類成員

對於類的大小,我們發現類內成員函式並不存在於類的儲存空間。這引發了我們的思考,類中的函式成員儲存在什麼地方?資料成員每個物件會有乙份,函式成員會不會也是這樣呢?include using namespace std class time private int hour int minute int...