C++第十五章_友元類_友元成員函數_異常機制_將對象做爲異常類型_棧解退_C風格字符串和C++字符串輸入問題

目錄

一、友元類ios

二、友元成員函數ide

三、異常機制函數

四、將對象做爲異常類型spa

五、棧解退.net

六、C風格字符串和C++字符串輸入問題3d

一、友元類

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
tv.h
 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 }
tv.cpp
 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 }
use_tv.cpp

 二、友元成員函數

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
tvfm.h

 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 */
error3.cpp

對於以上程序,注:

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;
exc_mean.h
 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 }
error4.cpp

五、棧解退

 棧解退定義:

 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;
exc_mean.h
  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 }
error5.cpp

程序執行過程以下:

狀況1:

狀況2:

 六、C風格字符串和C++字符串輸入問題

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。

相關文章
相關標籤/搜索