zoukankan      html  css  js  c++  java
  • C++第十五章_友元类_友元成员函数_异常机制_将对象作为异常类型_栈解退_C风格字符串和C++字符串输入问题

    目录

    1、友元类

    2、友元成员函数

    3、异常机制

    4、将对象作为异常类型

    5、栈解退

    6、C风格字符串和C++字符串输入问题

    1、友元类

    01)什么时候使用友元类?
         假如要编写一个电视机Tv类和一个遥控器Remote类,很显然,Tv类不是Remote类,Remote类也不是Tv类,即不存在is-a关系;
         遥控器Remote类也不是电视Tv类的一部分,反之亦然,因此包含、私有继承、保护继承的has-a关系也不存在;
         事实上,遥控器可以改变电视机的状态,这表明Remote类作为Tv类的一个友元。
    02)下面的语句使Remote成为友元类:

    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/86576169

     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

     2、友元成员函数

    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 };

    这里存在矛盾的地方:
    一、因为在Tv类中使用了Remote类,所以要让编译器能够处理这条语句,它必须知道Remote类的存在,否则编译器无法知道
          Remote类的存在,也就无法知道Remote类中的set_chan()方法,解决方法是让Remote定义在Tv定义之前;
    二、由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类中的私有变量;如下图所示:

     3、异常机制

    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块的函数

    4、将对象作为异常类型

     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!
    ";
    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

    5、栈解退

     栈解退定义:

     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
    ";
     17     }
     18     ~demo()
     19     {
     20         std::cout << "demo " << word << " destroyed
    ";
     21     }
     22     void show() const
     23     {
     24         std::cout << "demo " << word << " lives
    ";
     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!
    ";
     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()
    ";
    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:

     6、C风格字符串和C++字符串输入问题

    01)对于C风格字符串

    1 char info[100];
    2 cin >> info;  //读入一个单词
    3 cin.getline(info,100);  //读入一行,并直接从输入流中删除掉了键盘输入的换行符(可以接收空格).读入100个字符,并将info[100]设置为
    4 cin.getline(info,100,':');  //读入一行,直到遇到:,并且丢弃:
    5 cin.get(info,100);  //读入一行,把键盘输入的换行符留着了输入缓冲区中(可以接收空格).读入100个字符,并将info[100]设置为

    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)遇到分界字符(默认为 ),这时将把分界字符从输入流中删除它,但不存储它;
        (3)读取的字符数达到最大允许值,这时将设置输入流的fialbit,这意味着方法fail()将返回true。

  • 相关阅读:
    java中如何制作可双击执行的程序--jar打包工具的使用
    GUI菜单——菜单条、菜单、子条目之间关系
    设计模式——单例模式(饿汉式、懒汉式)
    深入理解利用new创建对象的执行过程以Person p=new Person("张三",20);为例
    构造代码块、构造函数、this执行顺序
    OpenGL 动画的制作
    UE4制作插件的插件神器pluginCreator
    3dmax中cs骨骼教程
    如何解决 应用程序无法启动 因为应用程序的并行配置不正确 问题
    如何自学游戏引擎的开发
  • 原文地址:https://www.cnblogs.com/YiYA-blog/p/11391716.html
Copyright © 2011-2022 走看看