詳解C++編程中類的成員變量和成員函數的相關知識
C++類的成員變量和成員函數
類是一種數據類型,它類似于普通的數據類型,但是又有別于普通的數據類型。類這種數據類型是一個包含成員變量和成員函數的一個集合。
類的成員變量和普通變量一樣,也有數據類型和名稱,占用固定長度的內存空間。但是,在定義類的時候不能對成員變量賦值,因為類只是一種數據類型,本身不占用內存空間,而變量的值則需要內存來存儲。
類的成員函數也和普通函數一樣,都有返回值和參數列表,它與一般函數的區(qū)別是:成員函數是一個類的成員,出現在類體中,它的作用范圍由類來決定;而普通函數是獨立的,作用范圍是全局的,或位于某個命名空間內。
上節(jié)我們在最后的完整示例中給出了 Student 類的定義,如下所示:
class Student{ public: //類包含的變量 char *name; int age; float score; public: //類包含的函數 void say(){ printf("%s的年齡是 %d,成績是 %f\n", name, age, score); } }; 上面的代碼在類體中定義了成員函數。你也可以只在類體中聲明函數,而將函數定義放在類體外面,如下圖所示: class Student{ public: char *name; int age; float score; public: void say(); //函數聲明 }; //函數定義 void Student::say(){ printf("%s的年齡是 %d,成績是 %f\n", name, age, score); }
在類體中直接定義函數時,不需要在函數名前面加上類名,因為函數屬于哪一個類是不言而喻的。
但當成員函數定義在類外時,就必須在函數名前面加上類名予以限定。::被稱為域解析符(也稱作用域運算符或作用域限定符),用來連接類名和函數名,指明當前函數屬于哪個類。
如果在域解析符“::”的前面沒有類名,或者函數名前面既無類名又無域解析符“::”,如:
//無類名 ::say( ){ //TODO } //無類名也無域解析符 say( ){ //TODO }
則表示 say() 函數不屬于任何類,這個函數不是成員函數,而是全局函數,即非成員函數的一般普通函數。
成員函數必須先在類體中作原型聲明,然后在類外定義,也就是說類體的位置應在函數定義之前,否則編譯時會出錯。
雖然成員函數在類的外部定義,但在調用時會根據在類中聲明的函數原型找到函數的定義(函數代碼),從而執(zhí)行該函數。
inline 成員函數
在類體中和類體外定義成員函數是有區(qū)別的:在類體中定義的成員函數為內聯(inline)函數,在類體外定義的不是。
內聯函數一般不是我們所期望的,它會將函數調用處用函數體替代,所以我建議在類體內部對成員函數作聲明,而在類體外部進行定義,這是一種良好的編程習慣。
當然,如果你的函數比較短小,希望定義為內聯函數,那也沒有什么不妥的。
如果你既希望將函數定義在類體外部,又希望它是內聯函數,那么可以在聲明函數時加 inline 關鍵字,如下所示:
class Student{ public: char *name; int age; float score; public: inline void say(); //聲明為內聯函數 }; //函數定義 void Student::say(){ printf("%s的年齡是 %d,成績是 %f\n", name, age, score); }
這樣,say() 就會變成內聯函數。
在類體內部定義的函數也可以加 inline 關鍵字,但這是多余的,因為類體內部定義的函數默認就是內聯函數。
值得注意的是,如果在類體外定義 inline 函數,則必須將類定義和成員函數的定義都放在同一個頭文件中(或者寫在同一個源文件中),否則編譯時無法進行嵌入(將函數代碼的嵌入到函數調用出)。這樣做雖然提高了程序的執(zhí)行效率,但從軟件工程質量的角度來看,這樣做并不是好的辦法,因此實際開發(fā)中較少在類中使用內聯函數。
C++提出內聯函數的主要用意是:用內聯函數取代帶參宏定義(函數傳參比宏更加方便易用),而不是提高程序運行效率,因為與執(zhí)行函數花費的時間相比,調用函數花費的時間往往微乎其微。
C++成員函數的存儲方式
用類去定義對象時,系統(tǒng)會為每一個對象分配存儲空間。如果一個類包括了數據和函數,要分別為數據和函數的代碼分配存儲空間。
按理說,如果用同一個類定義了10個對象,那么就需要分別為10個對象的數據和函數代碼分配存儲單元,如下圖所示。
能否只用一段空間來存放這個共同的函數代碼段,在調用各對象的函數時,都去調用這個公用的函數代碼。如圖所示。
顯然,這樣做會大大節(jié)約存儲空間。C++編譯系統(tǒng)正是這樣做的,因此每個對象所占用的存儲空間只是該對象的數據部分所占用的存儲空間,而不包括函數代碼所占用的存儲空間。如果聲明了一個類:
class Time { public: int hour; int minute; int sec; void set( ) { cin>>a>>b>>c; } };
可以用下面的語句來輸出該類對象所占用的字節(jié)數:
cout<<sizeof(Time)<<endl;
輸出的值是12。
這就證明了一個對象所占的空間大小只取決于該對象中數據成員所占的空間,而與成員函數無關。
函數代碼是存儲在對象空間之外的。如果對同一個類定義了10個對象,這些對象的成員函數對應的是同一個函數代碼段,而不是10個不同的函數代碼段。需要注意的是,雖然調用不同對象的成員函數時都是執(zhí)行同一段函數代碼,但是執(zhí)行結果一般是不相同的。
不同的對象使用的是同一個函數代碼段,它怎么能夠分別對不同對象中的數據進行操作呢?
原來C++為此專門設立了一個名為this的指針,用來指向不同的對象。需要說明:
不論成員函數在類內定義還是在類外定義,成員函數的代碼段都用同一種方式存儲。
不要將成員函數的這種存儲方式和inMne(內置)函數的概念混淆。不要誤以為用inline聲明(或默認為inline)的成員函數,其代碼段占用對象的存儲空間,而不用 inline聲明的成員函數,其代碼段不占用對象的存儲空間。不論是否用inline聲明,成員函數的代碼段都不占用對象的存儲空間。用inline聲明的作用是在調用該函數時,將函數的代碼段復制插人到函數調用點,而若不用inline聲明,在調用該函數時,流程轉去函數代碼段的人口地址,在執(zhí)行完該函數代碼段后,流程返回函數調用點。inline與成員函數是否占用對象的存儲空間無關,它們不屬同一個問題,不應搞混。
應當說明,常說的“某某對象的成員函數”,是從邏輯的角度而言的,而成員函數的存儲方式,是從物理的角度而言的,二者是不矛盾的。
相關文章
C++詳解使用floor&ceil&round實現保留小數點后兩位
這篇文章主要介紹了C++使用floor&ceil&round實現保留小數點后兩位的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07