第十七章 特殊类成员
第十七章 ?特殊類成員
1.1 ?靜態成員變量
假如我們要在一個類中的所有對象間共享某種數據,那不妨將其設置為靜態成員變量/函數;
static x
1.2 ?靜態成員變量
靜態成員變量與成員變量有4點不同:
①前者屬于類②前者必須在全局定義③前者被調用只用說明那個類即可④前者在沒有創建對象之前就已經存在
1 #include <iostream> 2 using namespace std; 3 class A 4 { 5 public: 6 static int n; 7 }; 8 int A::n=0; //要使用類成員限定來訪問靜態成員 9 void show(){cout<<A::n<<"a \n";} 10 int main() 11 { 12 int i; 13 for(i=0;i<5;i++) 14 { 15 A::n++; //訪問靜態成員變量n并對其進行自加操作............公有的可以直接訪問① 16 show(); //調用全局函數show,該函數輸出靜態成員變量n的值 17 } 18 return 0; 19 }?
2. ?私有靜態成員變量
1 #include <iostream> 2 using namespace std; 3 class A 4 { 5 public: 6 void func(){cout<<A::x<<endl;;} //定義公有成員函數func(),用該函數訪問私有靜態成員變量x 7 private: 8 static int x; //將靜態成員變量x聲明為私有 9 }; 10 int A::x=1000; //定義并初始化靜態成員變量 11 int main() 12 { 13 A a; //創建一個對象 14 a.func(); //用該對象訪問公有成員函數func() 15 //cout << A::x <<endl; //注意不能直接訪問...............私有的不可以直接訪問① 16 return 0; 17 }?
3. ?靜態成員函數
靜態成員函數在未創建對象時也可以使用,它的使用和靜態成員變量一樣 ①
1 /* 2 #include <iostream> 3 using namespace std; 4 class A 5 { 6 public: 7 void static show(){cout<<A::n;n++;} //注意:靜態成員函數不能訪問某個對象的成員變量,因為他沒有指向該對象的this指針。不過它可以訪問該類的靜態成員變量 ② 8 private: 9 static int n; //聲明私有靜態成員變量n 10 }; 11 int A::n=0; //定義私有靜態成員變量n 12 int main() 13 { 14 int i; 15 for(i=0;i<5;i++) 16 { 17 A::show(); //循環調用公有靜態成員函數show() 18 cout<<endl; 19 } 20 return 0; 21 } 22 */ 23 24 /* 25 //另外我們也可以通過對象來訪問靜態成員函數,如: 26 #include <iostream> 27 using namespace std; 28 class A 29 { 30 public: 31 void static show(){cout<<A::n;n++;} 32 private: 33 static int n; 34 }; 35 int A::n=0; 36 int main() 37 { 38 A a,b,c; 39 a.show(); 40 b.show(); 41 c.show(); 42 return 0; 43 } 44 */ 45 46 47 48 #include <iostream> 49 using namespace std; 50 class A 51 { 52 public: 53 void show(int i){x=i;cout<<x;} 54 //void static show1(int j){x=j;cout<<x;} 55 //靜態成員函數沒有指向對象的this指針,所以不能訪問對象的成員數據 56 private: 57 int x; 58 }; 59 int main() 60 { 61 A a; 62 a.show(1); 63 //a.show1(2); 64 //不能通過靜態成員函數訪問自己的成員變量 65 return 0; 66 }?
4. ?靜態成員的使用
靜態成員可以被繼承。
類中任何成員函數都可以訪問靜態成員,但是靜態成員函數不能直接訪問非靜態成員,靜態成員函數不能說明為虛函數
?
1 #include <iostream> 2 using namespace std; 3 class aspl //將阿司匹林聲明為一個aspl類,那么每箱阿司匹林就是該類的一個對象 4 { 5 public: 6 aspl(float p){price=p;TotalPrice=p+TotalPrice;} //在構造函數中實例化該對象的私有成員變量price,這樣就得到了一箱阿司匹林并且有了它的初始價格 7 ~aspl(){TotalPrice=TotalPrice-price;} //析構函數銷毀該對象并且將總價格減去該箱的價格,這樣賬面上就少了一箱阿司匹林,并且總價格也減去了該箱的價格 8 static float get(){return TotalPrice;} 9 private: 10 float price; //由于每箱阿司匹林都有價格,因此必須得有個成員變量來表示價格,這里在aspl這個類中聲明一個私有成員變量price 11 static float TotalPrice; //由于阿司匹林的總價格屬于類的總價格,而不是某一箱阿司匹林的價格,因此我們要將總價格聲明為靜態成員變量,這里聲明為TotalPrice 12 }; 13 float aspl::TotalPrice=0; //靜態成員變量必須初始化 14 void main() 15 { 16 float f; 17 cout<<"阿司匹林的庫存總價格為:"; 18 cout<<aspl::get()<<endl; //必須用類名限定符來調用靜態成員函數 19 int i=0; 20 cout<<"請輸入第"<<i+1<<"次購進的阿司匹林的單箱價格:"; 21 cin>>f; 22 aspl *p[5]; //定義了5個指向aspl類的數組指針p 23 p[i]=new aspl(f); //購進一箱阿司匹林 24 cout<<"阿司匹林的庫存總價格為:"; 25 cout<<aspl::get()<<endl; //輸出總價格 26 i++; //i代表購進的次數,i++表示將要進行i+1次購進 27 cout<<"請輸入第"<<i+1<<"次購進的阿司匹林的單箱價格:"; //提示用戶輸入i次購進 28 cin>>f; 29 p[i]=new aspl(f); //輸入的數值保存在i次購進的對象的成員變量中 30 cout<<"阿司匹林的庫存總價格為:"; 31 cout<<aspl::get()<<endl; //輸出當前的庫存總價格 32 cout<<"請輸入賣出的阿司匹林的編號,編號即第幾次購進:";//提示用戶要刪除哪次購進 33 cin>>i; //將輸入值保存在i變量中 34 delete p[i]; //刪除第i次創建的對象 35 cout<<"阿司匹林的庫存總價格為:"; 36 cout<<aspl::get()<<endl; //再次輸出銷售一箱阿司匹林后的庫存總價格 37 }?
?
?
5. ?函數指針
long(*func1)(int); //聲明了一個函數指針
long* func2(int); //聲明了一個返回指針的函數
第一種定義了一個叫func1的函數指針,該指針指向一個含有int型參數并且返回值為long型的函數
函數指針名可以看作函數名的代號,我們可以通過它來直接調用函數,所以函數指針經常會在條件或者判斷語句里出現,以便于用戶選擇調用不同名字但又類型和參數相同的函數。
另外要注意的是:函數指針可以指向某個函數,但是前提是被指向的函數的參數和返回至都與該函數指針被聲明時的返回值和參數相吻合
?
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 bool check(string str) //檢測是否是數字的函數,要注意該函數一定要放在調用函數的上面 5 { 6 for(int i = 0;i<str.length();i++) 7 if((str[i]>'9' || str[i]<'0')&&(str[i]!='.')) 8 return false; 9 return true; 10 } 11 float triangle(float &x,float &y) 12 { 13 return x*y*0.5; 14 } 15 float rectangle(float &x,float &y) 16 { 17 return x*y; 18 } 19 void Swap(float &x,float &y) 20 { 21 float n; 22 n=x; 23 x=y; 24 y=n; 25 } 26 void print(float &x,float &y) 27 { 28 cout<<"長為:"<<x<<" "<<"寬為:"<<y<<endl; 29 } 30 void get(float &a ,float &b) 31 { 32 cout<<"請輸入x的新值:"; 33 string str1;cin>>str1; 34 while(!check(str1)) //調用檢測數字函數,如果返回值為假,執行該循環,為真退出 35 { 36 cout<<"輸入的不是數字,請重新輸入!!!"<<endl; 37 cin>>str1; 38 } 39 a = atof(str1.c_str()); //將字符串轉換為浮點數 40 cout<<"請輸入y的新值:"; 41 string str2;cin>>str2; 42 while(!check(str2)){ 43 cout<<"輸入的不是數字,請重新輸入!!!"<<endl; 44 cin>>str2; 45 } 46 b = atof(str2.c_str()); 47 } 48 int main() 49 { 50 void(*p)(float &,float &); //聲明一個函數指針p,該指針指向一個返回void值并且帶有兩個float參數的函數 51 float(*fp)(float &, float &); //聲明一個函數指針fp,該指針指向一個返回float值并且帶有兩個float參數的函數 52 bool quit=false; 53 float a=2,b=3; //定義兩個參數a和b的值 54 int choice; //聲明選擇參數choice 55 while(quit==false) 56 { 57 cout<<"(0)退出(1)設定長寬(2)三角形(3)矩形(4)交換長寬:"; 58 cin>>choice; 59 switch(choice) //條件判斷語句 60 { 61 case 1: 62 p=get; //用指針p來指向函數名get,該函數帶有兩個float參數并返回一個void值,與函數指針p的參數和類型相吻合 63 break; 64 case 2: 65 fp=triangle; //用指針fp來指向函數名triangle,該函數帶有兩個float參數并返回一個float值,與函數指針fp的參數和類型相吻合 66 break; 67 case 3: 68 fp=rectangle; //用指針fp來指向函數名rectangle,該函數帶有兩個float參數并返回一個float值,與函數指針fp的參數和類型相吻合 69 break; 70 case 4: 71 p=Swap; //用指針p來指向函數名swap,該函數帶有兩個float參數并返回一個void值,與函數指針p的參數和類型相吻合 72 break; 73 default: 74 quit=true; 75 break; 76 } 77 if(quit)break; 78 if(choice==1||choice==4) //假如選擇了第1或者第4項 79 { 80 print(a,b); 81 p(a,b); //調用函數指針p所指向的函數,該指針指向的是一個返回值為void的函數,由于不同的選項中將不同的函數名賦給了指針p,因此選擇不同則調用的函數也不同 82 print(a,b); 83 } 84 else if(choice==2||choice==3) //假如選擇了第2和第3項 85 { 86 print(a,b); 87 cout<<"面積為:"<<fp(a,b)<<endl; //調用函數指針fp所指向的函數,該指針指向的是一個返回值為float的函數,由于不同的選項中將不同的函數名賦給了指針fp,因此選擇不同則調用的函數也不同 88 } 89 } 90 return 0; 91 }?
?
?
6. ?函數指針數組
void(*p[5])(float &,float &);
7. ?函數指針也可以作為函數的參數
1 #include <iostream> 2 using namespace std; 3 void square(int &x,int &y) 4 { 5 x=x*x; 6 y=y*y; 7 } 8 void cube(int &x,int &y) 9 { 10 x=x*x*x; 11 y=y*y*y; 12 } 13 void Swap(int &x,int &y) 14 { 15 int z; 16 z=x; 17 x=y; 18 y=z; 19 } 20 void print(void(*p)(int &x,int &y),int &x,int &y) //該函數有3個參數,第1個是一個函數指針p,它指向的函數帶有兩個參數, 21 //并返回一個void值,另外還有兩個int型引用x和y 22 { 23 cout<<"執行函數前\n"; 24 cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl; 25 p(x,y); 26 cout<<"執行函數后\n"; 27 cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl; 28 } 29 int main() 30 { 31 int a=2,b=3; 32 char choice; 33 bool quit=false; 34 void (*p)(int &,int &); 35 //聲明的p為一個函數指針,它所指向的函數帶有兩個參數并返回 一個void值 36 while(quit==false) 37 { 38 cout<<"(0)退出(1)平方(2)立方(3)交換參數:"; 39 cin>>choice; 40 switch(choice) 41 { 42 case '0':quit=true; 43 case '1':p=square;break; //輸入1,將函數名square的地址賦給p 44 case '2':p=cube;break; //輸入2,將函數名cube的地址賦給p 45 case '3':p=Swap;break; //輸入3,將函數名Swap的地址賦給p 46 default:p=0;break; 47 } 48 if(quit==true)break; 49 if(p==0) 50 { 51 cout<<"請輸入0到3之間的數字\n"; 52 continue; 53 } 54 print(p,a,b); //調用將函數指針作為參數的函數print 55 } 56 return 0; 57 }?
8. ?使用typedef簡化函數指針的聲明
1 //typedef 可以簡化代碼 可以為現有類型創建一個新的名字,或者聲明一個對象為某個新類型typedef void(*vp)(int &,int &); 2 //#define f(x) x*x ≠ #define f(x) (x*x) typedef要比#define要好,特別是在有指針的場合 再比如 3 // typedef char* pStr1; //后面代替前面 4 // #define pStr2 char* //沒有分號,前面代替后面 5 // pStr1 s1, s2; //定義了兩個char* 6 // pStr2 s3, s4; //定義了一個char* s3 一個char s4 7 // 8 9 #include <iostream> 10 using namespace std; 11 typedef void(*vp)(int &,int &);//typedef 將vp聲明為一個函數指針類型,該類型的指針指向一個帶有兩個int型引用參數并返回void的函數 12 void square(int &x,int &y) 13 { 14 x=x*x; 15 y=y*y; 16 } 17 void cube(int &x,int &y) 18 { 19 x=x*x*x; 20 y=y*y*y; 21 } 22 void Swap(int &x,int &y) 23 { 24 int z; 25 z=x; 26 x=y; 27 y=z; 28 } 29 void print(vp,int &,int &); //print函數的聲明部分,該函數有三個參數,一個vp類型的函數指針,兩個int型引用。 30 int main() 31 { 32 vp p; 33 int a=2,b=3; 34 char choice; 35 bool quit=false; 36 while(quit==false) 37 { 38 cout<<"(0)退出(1)平方(2)立方(3)交換參數:"; 39 cin>>choice; 40 switch(choice) 41 { 42 case '0':quit=true; 43 case '1':p=square;break; //輸入,將函數名square的地址賦給p 44 case '2':p=cube;break; //輸入,將函數名cube的地址賦給p 45 case '3':p=Swap;break; //輸入,將函數名Swap的地址賦給p 46 default:p=0;break; 47 } 48 if(quit==true)break; 49 if(p==0) 50 { 51 cout<<"請輸入0到3之間的數字\n"; 52 continue; 53 } 54 print(p,a,b); //調用這個將函數指針作為參數的函數 55 } 56 return 0; 57 } 58 void print(vp p,int &x,int &y) //print函數的定義部分,函數頭聲明了3個接收參數,第1個是vp類型的函數指針p,它指向的函數帶有兩個參數并返回一個void值,另外還有兩個int型引用x和y 59 { 60 cout<<"執行函數前\n"; 61 cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl; 62 p(x,y); 63 cout<<"執行函數后\n"; 64 cout<<"x:"<<x<<"\t"<<"y:"<<y<<endl; 65 }?
9. ?類的函數指針
1 #include <iostream> 2 using namespace std; 3 class human //抽象類human 4 { 5 public: 6 virtual void run()=0; //純虛函數run 7 virtual void eat()=0; //純虛函數eat 8 }; 9 class mother:public human //派生類mother從抽象類human繼承 10 { 11 public: 12 void run(){cout<<"母親跑百米要花二十秒\n";} //覆蓋純虛函數run 13 void eat(){cout<<"母親喜歡吃零食\n";} //覆蓋純虛函數eat 14 }; 15 class father: public human //派生類father從抽象類human繼承 16 { 17 public: 18 void run(){cout<<"父親跑百米要花十秒\n";} //覆蓋純虛函數run 19 void eat(){cout<<"父親不喜歡吃零食\n";} //覆蓋純虛函數eat 20 }; 21 class uncle:public human //派生類uncle從抽象類human繼承 22 { 23 public: 24 void run(){cout<<"舅舅跑百米要花十一秒\n";} //覆蓋純虛函數run 25 void eat(){cout<<"舅舅喜歡偷吃零食\n";} //覆蓋純虛函數eat 26 }; 27 int main() 28 { 29 void(human::*pf)()=0; //聲明一個成員函數指針pf,該指針屬于抽象類human 30 human* p=0; //聲明一個指向抽象類human的指針p,并將它的內存地址賦為0 31 char choice1,choice2; //聲明兩個字符變量,用來保存兩次用戶輸入的字符 32 bool quit=false; //聲明一個布爾變量quit作為while循環的條件 33 while(quit==false) //當quit為真時退出循環 34 { 35 cout<<"(0)退出(1)母親(2)父親(3)舅舅:"; //選擇菜單 36 cin>>choice1; //將用戶的第1次選擇保存在choice1中 37 switch(choice1) //將該選擇作為判斷的依據 38 { 39 case '0':quit=true;break; //假如輸入了字符0,那么將quit的值賦為真,然后退出switch循環 40 case '1':p=new mother;break; //假如輸入了字符1,那么創建mother類的新對象,并將p指向它,然后退出switch循環 41 case '2':p=new father;break; //假如輸入了字符2,那么創建father類的新對象,并將p指向它,然后退出switch循環 42 case '3':p=new uncle;break; //假如輸入了字符3,那么創建uncle類的新對象,并將p指向它,然后退出switch循環 43 default:choice1='q';break; //將字符q賦給choice1,然后退出switch循環 44 } 45 if(quit) //假如quit的值為真 46 break; //退出while循環 47 if(choice1=='q') //假如choice1的值為字符q 48 { cout<<"請輸入0到3之間的數字\n"; 49 continue; 50 } //輸出警告并跳轉到while循環的開始處繼續執行 51 cout<<"(1)跑步(2)進食\n"; //輸出選擇菜單 52 cin>>choice2; //將第2次用戶的選擇保存在choice2中 53 switch(choice2) //將用戶的第2次選擇作為判斷的依據 54 { 55 case '1':pf=&human::run;break; //假如輸入了字符1,那么將基類human的虛函數run的內存地址賦給成員函數指針,然后退出switch循環。注意,這里的&號是取human類成員函數run的地址 56 case '2':pf=&human::eat;break; //假如輸入了字符2,那么將基類human的虛函數eat的內存地址賦給成員函數指針,然后退出switch循環 57 default:break; //退出switch循環 58 } 59 (p->*pf)(); //通過指針p來訪問對象,通過*pf來訪問該對象的成員函數 60 delete p; //刪除p指針,因為*pf指向的不是對象而是該對象的成員函數,所以沒有必要刪除pf 61 } 62 return 0; 63 }?
10. ?成員函數指針數組
1 #include <iostream> 2 using namespace std; 3 class paper 4 { 5 public: 6 void read(){cout<<"紙上面的字可以讀\n";} 7 void write(){cout<<"紙可以用來寫字\n";} 8 void burn(){cout<<"紙可以用來點火\n";} 9 }; 10 typedef void(paper::*p)(); //利用typedef聲明一個成員函數指針類型p,該類型的指針指向paper類的成員函數,該函數不具返回值且沒有參數 11 int main() 12 { 13 //成員函數指針指向三個成員函數 14 p func[3]={&paper::read,&paper::write,&paper::burn};//用類型p來聲明一個func成員函數指針數組,并將它的成員函數指針初始化為它們所指向的函數的內存地址。 15 //與普通數組元素一樣,成員函數指針數組的每個成員函數指針也會擁有一個編號,該編號從0開始 16 paper* pp=0; //聲明一個指向paper類的普通指針 17 char choice[1]; //聲明一個只保存一個字符的char型數組choice 18 bool quit=false; //聲明一個布爾變量quit并將它的值賦為false 19 while(quit==false) //當quit的值為false時 20 { 21 cout<<"(0)退出(1)讀(2)寫(3)點火:"; //輸出選擇菜單 22 cin>>choice[0]; //將用戶的選擇保存在字符數組choice中 23 if(choice[0]>'3' || choice[0]<'0') //判斷該字符是否在0到3之間,假如不是 24 { 25 cout<<"請輸入從0~3之間的數字\n"; //提示用戶輸入 26 } 27 //否則,假如輸入的字符在0~3之間 28 else if (choice[0]=='0') //再判斷該字符是否等于'0' 29 { 30 quit=true; //等于的話將quit賦為true,那么while條件不成立,退出循環 31 } 32 else 33 { 34 int n; //定義一個整型變量用來接收被轉換為整型的字符串 35 pp=new paper; //新構造一個paper類對象,用pp來指向它 36 n=atoi(choice); //將choice轉換為整型后再賦給n 37 (pp->*func[n-1])(); // pp指針訪問新對象的成員函數,由該對象調用func 指針數組中下標為n-1的指針指向的成員函數,這里要注意數組的下標,n是用戶輸入的選項值,由于數組元素從0開始,所以要n減1,后面的()表示函數無參數 38 delete pp; //刪除pp指針指向的新對象 39 } 40 } 41 return 0; 42 }?
?
轉載于:https://www.cnblogs.com/zenseven/p/3799044.html
總結
以上是生活随笔為你收集整理的第十七章 特殊类成员的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Sql plus命令报command n
- 下一篇: Xcode中release和debug模