//站點:www. //所有稿件均有版權,如要轉(zhuǎn)載,請務必著名出處和作者 class test { private: int number; public: float socre; public: int rp() { return number; } void setnum(int a) { number=a; } }; 但是大家注意到?jīng)]有,標準c中是不允許在結(jié)構(gòu)體中聲明函數(shù)的,但c++中的類可以,這一點就和c有了本質(zhì)的區(qū)別,很好的體現(xiàn)了c++面向?qū)ο蟮奶攸c! 過去的c語言是一種非面向?qū)ο蟮恼Z言 他的特性是 程序=算法+數(shù)據(jù)結(jié)構(gòu) 但c++的特性是 對象=算法+數(shù)據(jù)結(jié)構(gòu) 程序=對象+對象+對象+對象+........ 所以根據(jù)這一特性,我們在定義一個自己定義的結(jié)構(gòu)體變量的時候,這個變量就應該是叫做對象或者叫實例 例如 test a; 那么a就是test結(jié)構(gòu)的一個對象(實例) test結(jié)構(gòu)體內(nèi)的成員可以叫做是分量,例如: a.socre=10.1f; 那么number就是test結(jié)構(gòu)的對象a的分量(或者叫數(shù)據(jù)成員,或者叫屬性)score; 在c語言中結(jié)構(gòu)體中的各成員他們的默認存儲控制是public 而 c++中類的默認存儲控制是private,所以在類中的成員如果需要外部掉用一定要加上關鍵字public聲明成公有類型,這一特性同樣使用于類中的成員函數(shù),函數(shù)的操作方式和普通函數(shù)差別并不大 例如上面的例子中的rp()成員函數(shù),我們?nèi)绻视腥缦露x test a; 的話,調(diào)用rp()就應該寫成 a.rp(); 成員函數(shù)的調(diào)用和普通成員變量的調(diào)用方式一致都采用.的操作符。 這一小節(jié)為了鞏固聯(lián)系我給出一個完整的例子。 如下(重要和特殊的地方都有詳細的注解): //程序作者:管寧 //站點:www. //所有稿件均有版權,如要轉(zhuǎn)載,請務必著名出處和作者 #include <iostream> using namespace std; class test { private://私有成員類外不能夠直接訪問 int number; public://共有成員類外能夠直接訪問 float socre; public: int rp() { return number; } void setnum(int a) { number=a; } }; void main() { test a; //a.number=10;//錯誤的,私有成員不能外部訪問 a.socre=99.9f; cout<<a.socre<<endl;//公有成員可以外部訪問 a.setnum(100);//通過公有成員函數(shù)setnum()間接對私有成員number進行賦值操作 cout<<a.rp();//間接返回私有成員number的值 cin.get(); } 好了,介紹了在類內(nèi)部定義成員函數(shù)(方法)的方法,下面我們要介紹一下域區(qū)分符(::)的作用了。 下面我們來看一個例子,利用這個例子中我們要說明兩個重要問題: //程序作者:管寧 //站點:www. //所有稿件均有版權,如要轉(zhuǎn)載,請務必著名出處和作者 #include <iostream> using namespace std; int pp=0; class test { private: int number; public: float socre; int pp; public: void rp(); }; void test::rp()//在外部利用域區(qū)分符定義test類的成員函數(shù) { ::pp=11;//變量名前加域區(qū)分符給全局變量pp賦值 pp=100;//設置結(jié)構(gòu)體變量 } void main() { test a; test b; a.rp(); cout<<pp<<endl; cout<<a.pp<<endl; cin.get(); } 問題1: 利用域區(qū)分符我們可以在類定義的外部設置成員函數(shù),但要注意的是,在類的內(nèi)部必須預先聲明: void test::rp() 在函數(shù)類型的后面加上類的名稱再加上域區(qū)分符(::)再加函數(shù)名稱,利用這樣的方法我們就在類的外部建立了一個名為rp的test類大成員函數(shù)(方法),可能很多人要問,這么做有意義嗎?在類的內(nèi)部寫函數(shù)代碼不是更好? 答案是這樣的:在類的定義中,一般成員函數(shù)的規(guī)模一般都比較小,而且一些特殊的語句是不能夠使用的,而且一般會被自動的設置成為inline(內(nèi)聯(lián))函數(shù),即使你沒有明確的聲明為inline,那么為什么有會被自動設置成為inline呢?因為大多數(shù)情況下,類的定義一般是放在頭文件中的,在編譯的時候這些函數(shù)的定義也隨之進入頭文件,這樣就會導致被多次編譯,如果是inline的情況,函數(shù)定義在調(diào)用處擴展,就避免了重復編譯的問題,而且把大量的成員函數(shù)都放在類中使用起來也十分不方便,為了避免這種情況的發(fā)生,所以c++是允許在外部定義類的成員函數(shù)(方法)的,將類定義和其它成員函數(shù)定義分開,是面向?qū)ο缶幊痰耐ǔW龇?,我們把類的定義在這里也就是頭文件了看作是類的外部接口,類的成員函數(shù)的定義看成是類的內(nèi)部實現(xiàn)。寫程序的時候只需要外部接口也就是頭文件即可,這一特點和我們使用標準庫函數(shù)的道理是一致的,因為在類的定義中,已經(jīng)包含了成員函數(shù)(方法)的聲明。 問題二 域區(qū)分符和外部全局變量和類成員變量之間的關系。 在上面的代碼中我們看到了,外部全局和類內(nèi)部都有一個叫做pp的整形變量,那么我們要區(qū)分操作他們用什么方法呢? 使用域區(qū)分符就可以做到這一點,在上面的代碼中::pp=11;操作的就是外部的同名稱全局變量,pp=100;操作的就是類內(nèi)部的成員變量,這一點十分重要! 問題三 一個類的所有對象調(diào)用的都是同一段代碼,那么操作成員變量的時候計算機有是如何知道哪個成員是屬于哪個對象的呢? 這里牽扯到一個隱藏的this指針的問題,上面的代碼在調(diào)用a.rp()的的時候,系統(tǒng)自動傳遞一了個a對象的指針給函數(shù),在內(nèi)部的時候pp=100;的時候其實就是this->pp=100; 所以你把上面的成員函數(shù)寫成如下形勢也是正確的: void test::rp() { ::pp=11; this->pp=100;//this指針就是指向a對象的指針 } 類的成員函數(shù)和普通函數(shù)一樣是可以進行重載操作的,關于重載函數(shù)前面已經(jīng)說過,這里不再說明。 給出例子仔細看: //程序作者:管寧 //站點:www. //所有稿件均有版權,如要轉(zhuǎn)載,請務必著名出處和作者 #include <iostream> using namespace std; class test { private: int number; public: float socre; int pp; public: void rp(int); void rp(float); }; void test::rp(int a)//在外部利用域區(qū)分符定義test類的成員函數(shù) { cout<<"調(diào)用成員函數(shù)!a:"<<a<<endl; } void test::rp(float a)//在外部利用域區(qū)分符定義test類的成員函數(shù) { cout<<"調(diào)用成員函數(shù)!a:"<<a<<endl; } void main() { test a; a.rp(100); a.rp(3.14f); cin.get(); } 下面我們來看一下利用指針和利用引用間接調(diào)用類的成員函數(shù),對于對于指針和引用調(diào)用成員函數(shù)和調(diào)用普通函數(shù)差別不大,在這里也就不再重復說明了,注意看代碼,多試多練習既可。 代碼如下: //程序作者:管寧 //站點:www. //所有稿件均有版權,如要轉(zhuǎn)載,請務必著名出處和作者 #include <iostream> using namespace std; class test { private: int number; public: float socre; int pp; public: int rp(int); }; int test::rp(int a)//在外部利用域區(qū)分符定義test類的成員函數(shù) { number=100; return a + number; } void run(test *p)//利用指針調(diào)用 { cout<<p->rp(100)<<endl; } void run(test &p)//利用引用 { cout<<p.rp(200)<<endl; } void main() { test a; run(&a); run(a); cin.get(); } 前面我們說過,類的成員如果不顯式的生命為public那么它默認的就是private就是私有的,私有聲明可以保護成員不能夠被外部訪問,但在c++還有一個修飾符,它具有和private相似的性能,它就是protected修飾符。 在這里我們簡單說明一下,他們?nèi)g的差別: 在類的private:節(jié)中聲明的成員(無論數(shù)據(jù)成員或是成員函數(shù))僅僅能被類的成員函數(shù)和友元訪問。 在類的protected: 節(jié)中聲明的成員(無論數(shù)據(jù)成員或是成員函數(shù))僅僅能被類的成員函數(shù),友元以及子類的成員函數(shù)和友元訪問。 在類的public:節(jié)中聲明的成員(無論數(shù)據(jù)成員或是成員函數(shù))能被任何人訪問。 由于private和protected的差別主要是體現(xiàn)在類的繼承中,現(xiàn)在的教程還沒有設計到友元和子類所以這里不做深入討論,但上面的三點務必記得,在以后的教程中我們會回過頭來說明的。 總的來說,類成員的保護無非是為了以下四點! 1.相對與普通函數(shù)和其它類的成員函數(shù)來說,保護類的數(shù)據(jù)不能夠被肆意的篡改侵犯! 2.使類對它本身的內(nèi)部數(shù)據(jù)維護負責,只有類自己才能夠訪問自己的保護數(shù)據(jù)! 3.限制類的外部接口,把一個類分成公有的和受保護的兩部分,對于使用者來說它只要會用就可以,無須了解內(nèi)部完整結(jié)構(gòu),起到黑盒的效果。 4.減少類與其它代碼的關聯(lián)程,類的功能是獨立的,不需要依靠應用程序的運行環(huán)境,這個程序可以用它,另外一個也可以用它,使得你可以輕易的用一個類替換另一個類。 下面為了演示類成員的保護特性,我們來做一個球類游戲! 我們設計一個類,來計算球員的平均成績,要求在外部不能夠隨意篡改球員的平均成績。 我們把該類命名為ballscore并且把它放到ballscore.h的有文件中! --------------------------- ballscore.h----------------------------- class ballscore { protected: const static int gbs = 5;//好球單位得分 const static int bbs = -3;//壞球單位扣分 float gradescore;//平均成績 public: float GetGS(float goodball,float badball)//goodball為好球數(shù)量,badball為壞求數(shù)量 { gradescore = (goodball*gbs + badball*bbs) / (goodball + badball); return gradescore;//返回平均成績 } }; -------------------------------------------------------------------- 主函數(shù)調(diào)用! #include <iostream> #include "ballscore.h" using namespace std; void main() { ballscore jeff; cout<<jeff.GetGS(10,3); jeff.gradescore=5.5//想篡改jeff的平均成績是錯誤的! cin.get(); } 在上面的代碼中頭文件和類的使用很好了體現(xiàn)了類的黑盒特性,誰也不能夠在外部修改球員的平均成績! 類體中的有一個地方要注意 const static int gbs = 5;//好球單位得分 const static int bbs = -3;//壞球單位扣分 之所以要修飾成const static 因為c++中類成員只有靜態(tài)整形的常量才能夠被初始化,到這里整個程序也就說完了,當然真正大比賽不可能是這樣,只是為了說明問題就題命題而已,呵呵! 下面我們說一下關于類的作用域。 在說內(nèi)容之前我們先給出這部分內(nèi)容的一個完整代碼,看講解的是參照此一下代碼! //程序作者:管寧 //站點:www. //所有稿件均有版權,如要轉(zhuǎn)載,請務必著名出處和作者 #include <iostream> using namespace std; class ballscore { protected: const static int gbs = 5;//好球單位得分 const static int bbs = -3;//壞球單位扣分 float gradescore;//平均成績 public: float GetGS(float goodball,float badball)//goodball為好球數(shù)量,badball為壞求數(shù)量 { int gradescore=0;//新定義一個和成員變量float gradescore相同名字的類成員函數(shù)局部變量 ballscore::gradescore = (goodball*gbs + badball*bbs) / (goodball + badball);//由于局部變量與類成員變量同名使用的時候必須在其前加上類名和域區(qū)分符 return ballscore::gradescore;//返回平均成績 } }; int ballscore=0;//定義一個與類名稱相同的普通全局變量 int test; void main() { class test//局部類的創(chuàng)建 { float a; float b; }; test test; ::test=1;//由于局部類名隱藏了外部變量使用需加域區(qū)分符 class ballscore jeff;//由于全局變量int ballsocre和類(ballsocre)名稱相同,隱藏了類名稱,這時候定義類對象需加class前綴以區(qū)分 cout<<jeff.GetGS(10,3); cin.get(); } 類的作用域是只指定義和相應的成員函數(shù)定義的范圍,在該范圍內(nèi),一個類的成員函數(shù)對同一類的數(shù)據(jù)成員具有無限制的訪問權。 在類的使用中,我們經(jīng)常會碰到以下三種情況: 1.類的成員函數(shù)的局部變量隱藏了類的同名成員變量,看如對上面程序的分析。 protected: const static int gbs = 5; const static int bbs = -3; float gradescore; public: float GetGS(float goodball,float badball) { int gradescore=0; ballscore::gradescore = (goodball*gbs + badball*bbs) / (goodball + badball); return ballscore::gradescore; } 代碼中的int gradescore就把float gradescore給隱藏了,所以要使用成員變量float gradescore的時候必須在其之前加上類名稱和域區(qū)分符(::)。 2.在類定義外部非類型名隱藏了類型名稱的情況,看上面代碼的分析! class ballscore { protected: const static int gbs = 5; const static int bbs = -3; float gradescore; public: float GetGS(float goodball,float badball) { int gradescore=0; ballscore::gradescore = (goodball*gbs + badball*bbs) / (goodball + badball); return ballscore::gradescore; } }; int ballscore=0; 代碼中的全部變量int ballscore隱藏了類名稱class ballscore 所以在main中如如果要定義ballscore類的對象就要在類名稱前加上class關鍵字 class ballscore jeff; 3.類型名稱隱藏了非類型名稱,看對上面代碼的分析! int test; void main() { class test { float a; float b; }; test test; ::test=1; class ballscore jeff; cout<<jeff.GetGS(10,3); cin.get(); } 在普通函數(shù)內(nèi)部定義的類叫做局部類,代碼中的test類就是一個局部類! 代碼中的test類隱藏了全局變量test如果要操作全局變量test那么就要在test前加上域區(qū)分符號(::),進行使用! ::test=1就是對全局變量test進行了賦值操作! 我們最后說一下名字空間! 名字空間就是指某一個名字在其中必須是唯一的作用域. 如果這個定義想不明白,可以簡單的說成,在一個區(qū)域內(nèi),某一個名字在里面使用必須是唯一的,不能出現(xiàn)重復定義的情況出現(xiàn),這個區(qū)域就是名字空間! c++規(guī)定: 1.一個名字不能同時設置為兩種不同的類型 class test { //... }; typedef int test; 這個就是錯誤的! 2.非類型名(變量名,常量名,函數(shù)名,對象名,枚舉成員)不能重名. test a; void a(); 就是錯誤的,因為a是一個test類的對象,它和函數(shù)a名稱重名了! 3.類型與非類型不在同一個名字空間上,可以重名,即使在同一作用域內(nèi),但兩者同時出現(xiàn)時定義類對象的時候要加上前綴class以區(qū)分類型和非類型名! class test { //..... } int test class test a;//利用class前墜區(qū)分,定義了一個test類的對象a 好了,到這里關于類的知識點我們已經(jīng)學習完,希望大家多多練習 |
|