一、友元類ios
二、友元成員函數ide
三、異常機制函數
四、將對象做爲異常類型spa
五、棧解退.net
01)何時使用友元類?
假如要編寫一個電視機Tv類和一個遙控器Remote類,很顯然,Tv類不是Remote類,Remote類也不是Tv類,即不存在is-a關係;
遙控器Remote類也不是電視Tv類的一部分,反之亦然,所以包含、私有繼承、保護繼承的has-a關係也不存在;
事實上,遙控器能夠改變電視機的狀態,這代表Remote類做爲Tv類的一個友元。
02)下面的語句使Remote成爲友元類:code
friend class Remote;
友元聲明能夠位於Tv類的公有、私有或保護部分,其位置可有可無;因爲Remote類中使用了Tv類的一些方法,因此要先聲明
Tv類,而後聲明Remote類;也可使用前向聲明,之後將介紹;
03)友元類的全部方法均可以訪問原始類的私有成員和保護成員,例如:對象
1 class A 2 { 3 friend class B; 4 5 private: int aa; 6 }; 7 8 class B 9 { 10 public: 11 void output() 12 { 13 cout << a.aa << endl; //直接訪問A類中的私有數據aa 14 } 15 private: A a; //建立A類對象 16 17 } 18 19 20 ———————————————— 21 版權聲明:本文爲CSDN博主「風雪殘存」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處連接及本聲明。 22 原文連接:https://blog.csdn.net/u012230798/article/details/86576169
參考博客:https://blog.csdn.net/u012230798/article/details/86576169blog
1 #ifndef TV_H_ 2 #define TV_H_ 3 4 class Tv 5 { 6 private: 7 int state; //on or off 8 int volume; //音量 9 int maxchannel; //頻道的最大數 10 int channel; //選擇的頻道 11 int mode; //Antenna or able 12 int input; //TV or DVD 13 public: 14 friend class Remote; //聲明Remote爲Tv的一個友元類 15 enum {Off, On}; //電視機的開關 16 enum {MinVal,MaxVal}; //聲音的最小和最大值 17 enum {Antenna, Cable}; //天線or電纜 18 enum {TV,DVD}; //TV模式orDVD模式 19 20 Tv(int s=Off,int mc=125) : state(s),volume(5),maxchannel(mc),channel(2),mode(Cable),input(TV) {}; 21 22 void onoff() {state = (state == On) ? Off:On;} //若state == On成立,則state=Off,不然state=On 23 bool ison() const {return state == On;} 24 bool volup(); //增大音量 25 bool voldown(); //減少音量 26 void chanup(); //頻道數增大 27 void chandown(); //頻道數減少 28 void set_mode() {mode = (mode == Antenna) ? Cable : Antenna;} 29 void set_input() {input = (input == DVD) ? TV:DVD;} 30 void settings() const; //diaplay all the settings 31 }; 32 33 /*Remote中的方法除了構造函數外,都調用了原始類Tv中的方法*/ 34 class Remote 35 { 36 private: 37 int mode; 38 public: 39 Remote(int m = Tv::TV) : mode(m) {} 40 bool volup(Tv & t) {return t.volup();} //直接調用Tv中的volup() 41 bool voldown(Tv & t) {return t.voldown();} 42 void onoff(Tv & t) {t.onoff();} 43 void chanup(Tv & t) {t.chanup();} 44 void chandown(Tv & t) {t.chandown();} 45 void set_chan(Tv & t,int c) {t.channel = c;} //友元類的全部方法(Tv & t也是)均可以訪問原始類的私有成員和保護成員 46 void set_mode(Tv & t) {t.set_mode();} 47 void set_input(Tv & t) {t.set_input();} 48 }; 49 50 #endif
1 //tv.cpp 2 #include <iostream> 3 #include "tv.h" 4 5 /*Tv類中增大音量函數*/ 6 bool Tv::volup() 7 { 8 if(volum < MaxVal) 9 { 10 volume++; 11 return true; 12 } 13 else 14 return false; 15 } 16 17 /*Tv類中減少音量函數*/ 18 bool Tv::voldown() 19 { 20 if(volume > MinVal) 21 { 22 volume--; 23 return true; 24 } 25 else 26 return false; 27 } 28 29 /*Tv類中增大頻道數函數*/ 30 void Tv::chanup() 31 { 32 if(channel < maxchannel) 33 channel++; 34 else 35 channel = 1; //增大到maxchannel後回到頻道1 36 } 37 38 /*Tv類中減少頻道數函數*/ 39 void Tv::chandown() 40 { 41 if(channel > 1) 42 channel--; 43 else 44 channel = maxchannel; //減少到頻道1後換到頻道最大值 45 } 46 47 /*diaplay all the settings*/ 48 void Tv::settings() const 49 { 50 using std::cout; 51 using std::endl; 52 53 cout << "Tv is " << (state = Off ? "Off" : "On") << endl; //此處則能夠省略下面if的else語句 54 if(state == On) //On是在Tv中定義的一個枚舉量 55 { 56 cout << "Volume setting = " << volume << endl; 57 cout << "Channel setting = " << channel <<endl; 58 cout << "Mode = " << (mode == Antenna ? "antenna":"cable") << endl; //在Tv類和Remote類中都定義了mode 59 cout << "Input = " << (input == DVD ? "DVD":"TV") << endl; 60 } 61 }
1 //use_tv.cpp 2 #include <iostream> 3 #include "tv.h" 4 5 int main() 6 { 7 using std::cout; 8 Tv s42; //此處將調用Tv類中的構造函數,因爲Tv類中的構造函數都有默認參數,因此此處至關於省略了參數而使用默認參數,來初始化s42 9 cout<<"Tv對象初始化設置:"<<endl; 10 s42.settings(); //顯示設置 11 12 s42.onoff(); //切換開關狀態 13 s42.chanup(); //頻道加 14 cout<<"使用電視機自己設置後的參數:"<<endl; 15 s42.sttings(); 16 17 Remote grey; //使用Remote類構造函數中的默認參數初始化並建立Remote對象 18 grey.volup(s42); //增大音量 19 grey.volup(s42); //再次增大音量 20 grey.chanup(s42); //頻道加 21 grey.set_chan(s42.5); //設置頻道5; 22 cout<<"使用遙控器設置後的參數:"<<endl; 23 s42.settings(); 24 25 Tv s58(Tv::On); //使用參數On建立Tv對象s58 26 s58.set_mode(); 27 s58.settings() 28 29 }
01)從上一個例子中咱們能夠看出,在Remote類中只有Remote::set_chan()方法直接影響了Tv類中的私有數據(channel);
而友元函數的特色之一就是能夠訪問類的私有數據(P391),因此咱們能夠只讓Remote::set_chan()方法成爲Tv類的友元,
而不是讓Remote類中的全部方法都成爲Tv類中的友元函數。
02)讓Remote::set_chan()成爲Tv類的友元方法是在Tv類中作以下聲明:排序
1 class Tv 2 { 3 friend void Remote::set_chan(Tv & t, int c); 4 ... 5 };
這裏存在矛盾的地方:
1、由於在Tv類中使用了Remote類,因此要讓編譯器可以處理這條語句,它必須知道Remote類的存在,不然編譯器沒法知道
Remote類的存在,也就沒法知道Remote類中的set_chan()方法,解決方法是讓Remote定義在Tv定義以前;
2、由friend void Remote::set_chan(Tv & t, int c)可知,在set_chan()方法中使用了Tv類對象,這意味着Tv定義應該放在Rmeote
定義以前。
解決以上矛盾的方法是使用前向聲明:class Tv;
這樣聲明的排序次序應以下:
1 class Tv; //前向聲明 2 class Remote {...}; 3 class Tv {...};
可否像下面這樣排序呢?
1 class Remote; 2 class Tv {...}; 3 class Remote {...};
答案是不能。由於Remote類中的set_chan()方法在Tv中用到,因此必須先聲明Remote類,再聲明Tv類,即要將Tv類作前向聲明。
03)還有一個問題,例如在Remote類中一個函數的定義以下:
void onoff(Tv & t) {t.onoff();}
在Remote類中將調用Tv類中的方法,然而Tv類證實在Remote類聲明以後,解決方法是在Remote類中只包含類的聲明,不包含
類方法的定義便可.
注:tvfm.h包含了函數聲明和方法定義:
1 #ifndef TVFM_H_ 2 #define TVFM_H_ 3 4 class Tv; //前向聲明 5 6 class Remote 7 { 8 public: 9 enum State{Off, On}; //電視機的開關 10 enum {MinVal,MaxVal}; //聲音的最小和最大值 11 enum {Antenna, Cable}; //天線or電纜 12 enum {TV,DVD}; //TV模式orDVD模式 13 private: 14 int mode; 15 public: 16 Remote(int m = Tv::TV) : mode(m) {} 17 bool volup(Tv & t); //除了構造函數外,均只聲明方法,由於Tv定義在Rmeote聲明在Tv聲明以後 18 bool voldown(Tv & t); 19 void onoff(Tv & t); 20 void chanup(Tv & t); 21 void chandown(Tv & t); 22 void set_chan(Tv & t,int c); 23 void set_mode(Tv & t); 24 void set_input(Tv & t); 25 } 26 27 class Tv 28 { 29 private: 30 int state; //on or off 31 int volume; //音量 32 int maxchannel; //頻道的最大數 33 int channel; //選擇的頻道 34 int mode; //Antenna or able 35 int input; //TV or DVD 36 public: 37 friend void Remote::set_chan(Tv & t, int c); //聲明Remote中的set_chan()方法爲Tv的一個友元函數 38 39 enum {Off, On}; //電視機的開關 40 enum {MinVal,MaxVal}; //聲音的最小和最大值 41 enum {Antenna, Cable}; //天線or電纜 42 enum {TV,DVD}; //TV模式orDVD模式 43 44 Tv(int s=Off,int mc=125) : state(s),volume(5),maxchannel(mc),channel(2),mode(Cable),input(TV) {}; 45 46 void onoff() {state = (state == On) ? Off:On;} //若state == On成立,則state=Off,不然state=On 47 bool ison() const {return state == On;} 48 bool volup(); //增大音量 49 bool voldown(); //減少音量 50 void chanup(); //頻道數增大 51 void chandown(); //頻道數減少 52 void set_mode() {mode = (mode == Antenna) ? Cable : Antenna;} 53 void set_input() {input = (input == DVD) ? TV:DVD;} 54 void settings() const; //diaplay all the settings 55 }; 56 57 /* 58 01)Remote類中的方法定義,加上關鍵字inline使全部方法均成爲內聯函數 59 02)必須將定義放在後面,由於在Remote中的方法中要使用Tv對象 60 */ 61 inline bool Remote::volup(Tv & t) {return t.volup();} //直接調用Tv中的volup() 62 inline bool Remote::voldown(Tv & t) {return t.voldown();} 63 inline void Remote::onoff(Tv & t) {t.onoff();} 64 inline void Remote::chanup(Tv & t) {t.chanup();} 65 inline void Remote::chandown(Tv & t) {t.chandown();} 66 inline void Remote::set_chan(Tv & t,int c) {t.channel = c;} //友元函數的全部方法(Tv & t也是)均可以訪問原始類的私有成員和保護成員 67 inline void Remote::set_mode(Tv & t) {t.set_mode();} 68 inline void Remote::set_input(Tv & t) {t.set_input();} 69 #endif
Remote類成爲Tv類的友元類:Remote中的全部方法都可以影響Tv類中的私有成員;以下圖所示:
只讓Remote中的set_chan()方法成爲Tv類的友元函數,則只有Remote::set_chan()能夠影響Tv類中的私有變量;以下圖所示:
01)調用abort()函數,需包含頭文件#include <cstdlib>;在程序中調用abort()會打印一個隨系統而異的文字
02)異常機制:使用try、throw、catch關鍵字
1 #include <iostream> 2 3 double hmean(double a, double b); //聲明一個函數 4 5 int main() 6 { 7 double x,y,z; 8 9 std::cout<<"Enter two numbers: "; 10 while(cin>>x>>y) 11 { 12 try 13 { 14 z = hmean(x,y); 15 } 16 catch(const char *s) //catch是關鍵字,並非函數; throw後的string會賦給s;若是沒有執行throw(即沒有異常)會忽略catch中的語句 17 { 18 std::cout << s << std::endl; 19 std::cout << "Enter a new pair of numbes: "; 20 continue; //返回到while繼續執行 21 } 22 std::cout<<"Harmonic mean of " << x << " and " << y << " is " << z <<std::endl; 23 std::cout << "Enter next set of numbers <q to qiut>" << std::endl; //繼續執行while循環 24 } 25 26 std::cout << "Bye!" << std::endl; 27 return 0; 28 } 29 30 double hmean(double a, double b) 31 { 32 if(a == -b) 33 throw "Bad hmean() arguments: a=-b not allowed"; //因爲throw後的字符串和main()中catch中的char *s匹配,因此執行throw後會執行catch 34 return 2.0*a*b/(a+b); 35 } 36 /* 37 01)若輸入的兩個數字爲6,10,則直接執行try中的語句,執行完畢後,跳過catch中的語句; 38 02)若輸入的兩個數字爲6,-6,則執行try中的hmean(),後執行throw,而後跳轉到main()中的catch,執行catch中的語句 39 03)程序執行到throw後,程序會沿函數調用的順序後退,直到遇到try塊的函數 40 */
對於以上程序,注:
01)若輸入的兩個數字爲6,10,則直接執行try中的語句,執行完畢後,跳過catch中的語句;
02)若輸入的兩個數字爲6,-6,則執行try中的hmean(),後執行throw,而後跳轉到main()中的catch,執行catch中的語句
03)程序執行到throw後,程序會沿函數調用的順序後退,直到遇到try塊的函數
1 /*將對象做爲異常類型*/ 2 #include <iostream> 3 4 class bad_hmean 5 { 6 private: 7 double v1; 8 double v2; 9 public: 10 bad_hmean(double a=0, double b=0) : v1(a),v2(b) {} //使用默認值初始化私有變量,建立對象時,可使用新值覆蓋默認值 11 void mesg(); //用於報告錯誤信息 12 }; 13 inline void bad_hmean::mesg() 14 { 15 std::cout << "hmean(" << v1 << ", " << v2 << "):" << "invalid arguments: a=-b not allowed!" << std::endl; 16 } 17 18 class bad_gmean 19 { 20 private: 21 double v1; 22 double v2; 23 public: 24 bad_gmean(double a=0, double b=0) : v1(a),v2(b) {} //使用默認值初始化私有變量,建立對象時,可使用新值覆蓋默認值 25 const char* mesg(); //用於報告錯誤信息 26 }; 27 inline const char* bad_gmean::mesg() 28 { 29 return "gmean() arguments should be >=0" << std::endl;
1 /*error4.cpp for exc_mean.h*/ 2 #inlude <iostream> 3 #include "exc_mean.h" 4 5 using std::cin; 6 using std::cout: 7 using std::endl; 8 9 double hmean(double a, double b); 10 double gmean(double a, double b); 11 12 int main() 13 { 14 double x,y,z; 15 16 while(cin>>x>>y) 17 { 18 try 19 { 20 z = hmean(x,y); 21 z = gmean(x,y); 22 } 23 catch(bad_hmean & b) //與hmean()中的throw匹配 24 { 25 b.mesg(); 26 cout << "繼續輸入數字或者是按下任意字母退出程序!"<< endl; 27 continue; //會到while循環 28 } 29 catch(bad_gmean & h) //與gmean()中的throw匹配 30 { 31 h.mesg(); 32 cout << "Try again!" << endl; 33 cout << "繼續輸入數字或者是按下任意字母退出程序!"<< endl;//若是輸入的是字母,則while中的條件會判斷不成立從而退出循環 34 continue; //會到while循環 35 } 36 } 37 cout << "Bye!\n"; 38 return 0; 39 } 40 41 double hmean(double a, double b) 42 { 43 bad_hmean bh(a,b); //使用a、b建立並初始化類對象bh 44 if(a == -b) 45 throw bh; //若是執行該語句,則返回到main()中的catch(bad_hmean & b)處繼續執行 46 return 2.0*a*b/(a+b); 47 } 48 double gmean(double a, double b) 49 { 50 bad_gmean bg(a,b); //使用a、b建立並初始化類對象bg 51 if(a<0 || b<0) 52 throw bg; //若是執行該語句,則返回到main()中的catch(bad_gmean & h)處繼續執行 53 return std::sqrt(a*b); 54 }
棧解退定義:
1 /*將對象做爲異常類型*/ 2 #include <iostream> 3 4 class bad_hmean 5 { 6 private: 7 double v1; 8 double v2; 9 public: 10 bad_hmean(double a=0, double b=0) : v1(a),v2(b) {} //使用默認值初始化私有變量,建立對象時,可使用新值覆蓋默認值 11 void mesg(); //用於報告錯誤信息 12 }; 13 inline void bad_hmean::mesg() 14 { 15 std::cout << "hmean(" << v1 << ", " << v2 << "):" << "invalid arguments: a=-b not allowed!" << std::endl; 16 } 17 18 class bad_gmean 19 { 20 private: 21 double v1; 22 double v2; 23 public: 24 bad_gmean(double a=0, double b=0) : v1(a),v2(b) {} //使用默認值初始化私有變量,建立對象時,可使用新值覆蓋默認值 25 const char* mesg(); //用於報告錯誤信息 26 }; 27 inline const char* bad_gmean::mesg() 28 { 29 return "gmean() arguments should be >=0" << std::endl;
1 /*棧解退*/ 2 #include <iostream> 3 #include <cmath> //for sqrt() 4 #include <string> 5 #include "exc_mean.h" 6 7 /*定義一個類*/ 8 class demo 9 { 10 private: 11 std::string word; 12 public: 13 demo(const std::string & str) //構造函數的定義 14 { 15 word = str; 16 std::cout << "demo " << word << " created\n"; 17 } 18 ~demo() 19 { 20 std::cout << "demo " << word << " destroyed\n"; 21 } 22 void show() const 23 { 24 std::cout << "demo " << word << " lives\n"; 25 } 26 }; 27 28 /*聲明函數*/ 29 double hmean(double a, double b); 30 double gmean(double a, double b); 31 double means(double a, double b); 32 33 int main() 34 { 35 using std::cout; 36 using std::cin; 37 using std::endl; 38 39 double x,y,z; //使用一個函數塊 40 { 41 demo d1("found in block in main()"); //建立demo對象 42 cout << "Enter two numbers: "; 43 while(cin>>x>>y) 44 { 45 try //正常執行完try中的語句後會自動回到while處繼續執行 46 { 47 z = means(x,y); 48 cout << "The mean mean of " << x << " and " <<y << " is " << z << endl; 49 cout << "繼續輸入數字或者是按下任意字母退出程序!"<< endl;//若是輸入的是字母,則while中的條件會判斷不成立從而退出循環 50 } 51 catch(bad_hmean & b) //與hmean()中的throw匹配 52 { 53 b.mesg(); 54 cout << "繼續輸入數字或者是按下任意字母退出程序!"<< endl; 55 continue; //會到while循環 56 } 57 catch(bad_gmean & h) //與gmean()中的throw匹配 58 { 59 h.mesg(); 60 cout << "Try again!" << endl; 61 cout << "繼續輸入數字或者是按下任意字母退出程序!"<< endl;//若是輸入的是字母,則while中的條件會判斷不成立從而退出循環 62 continue; //會到while循環 63 } 64 } 65 } 66 cout << "Bye!\n"; 67 cin.get(); //使dos界面中止 68 return 0; 69 } 70 71 /*函數定義*/ 72 double hmean(double a, double b) 73 { 74 bad_hmean bh(a,b); //使用a、b建立並初始化類對象bh 75 if(a == -b) 76 throw bh; //若是執行該語句,則返回到means()中的catch(bad_hmean & b)處繼續執行 77 //由於程序是沿着函數調用的反方向去尋找try塊,找到了try塊以後,程序會到try塊的最後處繼續執行(即執行try塊後面的語句) 78 return 2.0*a*b/(a+b); 79 } 80 double gmean(double a, double b) 81 { 82 bad_gmean bg(a,b); //使用a、b建立並初始化類對象bg 83 if(a<0 || b<0) 84 throw bg; //若是執行該語句,則去means()中找有沒有catch(bad_gmean & h)處繼續執行,若沒有則到main()中去找與bg匹配的catch 85 //由於程序是沿着函數調用的反方向去尋找try塊,找到了try塊以後,程序會到try塊的最後處繼續執行(即執行try塊後面的語句) 86 return std::sqrt(a*b); 87 } 88 double means(double a, double b) 89 { 90 double am,bm,gm; 91 demo d2("found in means()"); //建立另一個demo對象 92 am = (a+b)/2.0; 93 try 94 { 95 hm = hmean(a,b); 96 gm = gmean(a,b); 97 } 98 catch(bad_hmean & b) 99 { 100 b.mesg(); //調用bad_hmean類中的mesg() 101 std::cout << "Caught in means()\n"; 102 throw; //該throw會致使means()終止執行,並將標識爲bad_hmean & b(bad_hmean類對象)傳遞給main()中的catch(bad_hmean & b) 103 } 104 d2.show(); //假如上面的throw 被執行,則不會執行該句 105 return (am+bm+gm)/3.0; 106 }
程序執行過程以下:
狀況1:
狀況2:
01)對於C風格字符串
1 char info[100]; 2 cin >> info; //讀入一個單詞 3 cin.getline(info,100); //讀入一行,並直接從輸入流中刪除掉了鍵盤輸入的換行符(能夠接收空格).讀入100個字符,並將info[100]設置爲\0 4 cin.getline(info,100,':'); //讀入一行,直到遇到:,而且丟棄: 5 cin.get(info,100); //讀入一行,把鍵盤輸入的換行符留着了輸入緩衝區中(能夠接收空格).讀入100個字符,並將info[100]設置爲\0
02)對於string對象(有兩種方式輸入)
1 string stuff; 2 cin >> stuff; //讀入一個單詞 3 getline(cin,stuff); //讀入一行,並從輸入流中刪除掉了鍵盤輸入的換行符(能夠接收空格) 4 getline(stuff,':'); //讀入一行,直到遇到:,而且丟棄:
string版本的getline()將自動調整string對象的大小,使之恰好可以存儲輸入的字符,且不須要指定讀取多少個字符的數值參數
03)string結束讀入的條件:
(1)到達文件尾。此時輸入流的eofbit將被設置,意味着方法fial()和eof()都將返回true;
(2)遇到分界字符(默認爲\n),這時將把分界字符從輸入流中刪除它,但不存儲它;
(3)讀取的字符數達到最大容許值,這時將設置輸入流的fialbit,這意味着方法fail()將返回true。