• C++ Primer Plus 笔记第八章


      

    C++内联函数:

       内联函数的编译代码与其他程序代码 “内联” 起来了,编译器将使用相应的函数代码替换函数调用;

       对于内联函数,程序无需跳到另一个位置处执行代码,再跳回来:

        内联函数的运行速度比常规函数稍快;

        但是需要占用更多的内存

       使用内联函数:

        1. 在函数声明前加上关键字 inline;

        2. 在函数定义前加上关键字 inline;

        3. 通常做法是省略原型,将整个定义(函数头和所有函数代码)放在本应提供原型的地方 

       内联函数与常规函数一样,也是按值传递参数

    引用变量:

       C++新增了一种复合类型——引用变量,引用是已定义的变量的别名

       引用变量的主要用途是用作函数的形参,通过引用变量用作参数,函数使用原始数据,而不是拷贝数据;

    创建引用变量:

       int rats;

       int & rodents = rats;  // 此处引用声明允许将 rats 和 radents 互换——他们指向相同的值和内存单元 

       int & 表示的是指向 int 的引用

       记住:必须在声明引用时将其初始化(所以引用更接近与 const 指针)

    将引用用作函数参数:

       按引用传递允许被调用函数能够访问调用函数中的变量(C语言只能按值传递(还可以指针)——拷贝);

       参数传递对比:

        调用:

            swapr ( wallet1, wallet2);    // 引用传递调用

            swapp ( &wallet1, &wallet2 );  // 指针传递调用

            awapv (wallet1, wallet2);    // 按值传递调用

        原型:

            void ( int & a, int & b);        // 引用调用原型,变量a,b是 wallet1 和 wallet2 的别名

            void ( int * a, int * b);       // 指针传递原型,需要在函数使用 p 和 q 的整个过程使用解除引用操作符 *

            void ( int a, int b);            // 按值传递原型,变量 a,b 是参数 wallet1 和 wallet2 的拷贝,互补影响

       被调用函数中引用变量修改,会改变调用函数中的值,如不想修改则应使用常量引用:

        double refcube ( const double &ra );  // 这样做,当编译器发现代码修改了 ra 的值时,将发生错误消息

    临时变量、引用参数和 const:

       如果实参与形参不匹配,C++将生成临时变量(仅当参数为 const 引用时):

        1. 实参的类型正确,但不是左值;

        2. 实参的类型不正确,但可以转换成正确的类型

    尽可能使用 const:

       1. 使用 const 可以避免无意中修改数据的编程错误;

       2. 使用 const 使函数能够处理 const 和非 const 实参,否则将只能接受非 const 数据;

       3. 使用 const 引用使函数能够正确生成并使用临时变量

    何时使用引用参数:

       使用引用参数的原因:

        1. 程序员能够修改调用函数中的数据对象;

        2. 通过传递引用而不是整个数据对象,可以提高程序运行速度(数据对象较大时更重要)

       对于使用传递的值而不作修改的函数:

        1. 如果数据对象很小,如内置数据类型或小型结构,按值传递;

        2. 如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向 const 的指针;

        3. 如果数据对象是较大的结构,则使用 const 指针或 const 引用,可以节省复制结构所需的时间和空间;

        4. 若果数据对象是类对象,则使用 const 引用。传递类对象参数的标准方式是按引用传递

       对于修改调用函数中数据的函数:

        1. 如果数据对象是内置数据类型,则使用指针;

        2. 如果数据对象是数组,则只能使用指针;

        3. 如果数据对象是结构,则使用引用或指针;

        4. 如果数据对象是类对象,则使用引用

    将引用用于结构:

       引用非常适用于结构和类(C++的用户自定义类型),引入引用主要是为了用于这些类型,而不是基本的内置类型;

       例程:

     1 #include<iostream>
     2 using namespace std;
     3 struct sysop {
     4     char name[26];
     5     char quote[64];
     6     int used;
     7 };
     8 const sysop & use(sysop & sysopref);
     9 
    10 int main()
    11 {
    12     sysop looper = 
    13     {
    14         "Rick "Fortan" Looper",
    15         "I`m a goto kind of guy.",
    16         0
    17     };
    18 
    19     use(looper);
    20     cout << "Looper: " << looper.used << " use(s)
    ";
    21 
    22     sysop copycat;
    23     copycat = use(looper);
    24     cout << "Looper: " << looper.used << " use(s)
    ";
    25     cout << "copycat: " << copycat.used << " use(s)
    ";
    26     cout << "use(looper): " << use(looper).used << " use(s)
    ";
    27 
    28     return 0;
    29 }
    30 
    31 const sysop & use(sysop & sysopref)
    32 {
    33     cout << sysopref.name << " says:
    ";
    34     cout << sysopref.quote << endl;
    35     sysopref.used++;
    36     return sysopref;
    37 }

        函数说明:

        1. 使用指向结构的引用

          use ( looper );

          函数调用结构 looper 按引用传递给 use() 函数,使得 sysopref 成为 looper 的别名

        2. 将引用作为返回值

          通常,返回机制将返回值复制到临时存储区域中,随后调用程序将访问该区域;

          返回引用意味着调用程序将直接访问返回值,而不需要拷贝;

          通常,引用将指向传递给函数的引用,因此调用函数实际上是直接访问自己的一个变量

        记住: 返回引用的函数实际上是被引用变量的别名

        3. 使用函数调用来访问结构成员

          cout << "use(looper): " << use(looper).used << " use(s) ;

          函数 use() 返回一个指向 looper 的引用,因此上述代码与下面两行代码等效:

          use ( looper );

          cout  << " use(looper):  " << looper.used << " use(s) ";

     返回引用时需要注意的问题:

       避免返回当函数终止时不再存在的内存单元引用(返回引用时最重要的一点)

        1. 返回一个作为参数传递给函数的引用,将指向调用函数使用的数据,因此返回的引用也指向这些数据

        2. 使用 new 来分配新的存储空间:

          sysop * psysop = new sysop;

     为何将 const 用于引用返回类型:

       const sysop & use(sysop & sysopref);

       const sysop & 表示不能使用返回的引用来直接修改它指向的结构

       省略 const 的情况:

        use ( looper ).used = 10;

        由于 use() 返回一个指向 looper 的引用,上述代码将与下面的代等效:

        use ( looper );

        looper.used = 1;

         省略 const 后,可以编写更简短但含义更模糊的代码

       通常,将返回类型声明为 const 引用,可以减程序的模糊特性

    将引用用于类对象:

       将类对象传递给函数时,C++通常做法是使用引用。例如可以通过使用引用,让函数将类 string、ostream、istream、ofstream和ifstream等类对象作为参数

       将引用用于 string 类例程: 

     1 #include<iostream>
     2 #include<string>
     3 using namespace std;
     4 
     5 string version1(const string & s1, const string & s2);
     6 const string & version2(string & s1, const string & s2);
     7 //const string & version3(string & s1, const string & s2);
     8 
     9 int main()
    10 {
    11     string input;
    12     string copy;
    13     string result;
    14 
    15     cout << "Enter a string: ";
    16     getline(cin, input);
    17     copy = input;
    18     cout << "Your string as entered: " << input << endl;
    19     result = version1(input, "***");
    20     cout << "Your string enhanced: " << result << endl;
    21     cout << "Your original string: " << input << endl;
    22 
    23     result = version2(input, "***");
    24     cout << "Your string enhanced: " << result << endl;
    25     cout << "Your original string: " << input << endl;
    26 
    27     return 0;
    28 }
    29 
    30 string version1(const string & s1, const string & s2)
    31 {
    32     string temp;
    33     temp = s2 + s1 + s2;
    34     return temp;
    35 }
    36 
    37 const string & version2(string & s1, const string &s2)
    38 {
    39     s1 = s2 + s1 + s2;
    40     return s1;
    41 } 

       可以将C-风格字符串用作 string 对象引用参数:

        如果形参类型为 const string &,在调用函数时,使用的实参可以是 string 对象或C-风格字符串:

          因此代码 result = version1(input, "***");  将 "***" 赋给string对象是可行的

    对象、继承和引用:

       将特性从一个类传递给另一个类的语言特性被称为继承

       ostream 是基类,ofstream 是派生类,派生类继承了基类方法,意味着 ofstream 可以使用基类的特性,如格式化方法 precision(),setf();

       基类引用可以指向派生类对象,而无需进行强制类型转换:

        可以定义一个接收基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数也可以将派生类对象作为参数;

        参数类型为 ostream & 的函数可以接受 ostream 对象(如 cout)或自己声明的 ofstream 对象作为参数

       例程:

     1 #include<iostream>
     2 #include<fstream>
     3 #include<cstdlib>
     4 using namespace std;
     5 
     6 void file_it(ostream & os, double fo, const double fe[], int n);
     7 const int LIMIT = 5;
     8 
     9 int main(void)
    10 {
    11     ofstream fout;
    12     const char * fn = "ep-data.txt";
    13     fout.open(fn);
    14     if (!fout.is_open())
    15     {
    16         cout << "Can`t open " << fn << ". Bye.
    ";
    17         exit(EXIT_FAILURE);
    18     }
    19     double objective;
    20     cout << "Enter the focal length of your telescope objective in mm: ";
    21     cin >> objective;
    22     double eps[LIMIT];
    23     cout << "Enter the focal lengths, in mm, of " << LIMIT << " eyepieces:
    ";
    24     for (int i = 0; i < LIMIT; i++)
    25     {
    26         cout << "Eyepiece #" << i + 1 << ": ";
    27         cin >> eps[i];
    28     }
    29     file_it(fout, objective, eps, LIMIT);
    30     file_it(cout, objective, eps, LIMIT);
    31     cout << "Done
    ";
    32     return 0;
    33 }
    34 
    35 void file_it(ostream & os, double fo, const double fe[], int n)
    36 {
    37     ios_base::fmtflags initial;
    38     initial = os.setf(ios_base::fixed);  // save initial formatting state
    39     os.precision(0);
    40     os << "Focal length of objective: " << fo << " mm
    ";
    41     os.setf(ios::showpoint);
    42     os.precision(1);
    43     os.width(12);
    44     os << "f eyepiece";
    45     os.width(15);
    46     os << "magnification" << endl;
    47     for (int i = 0; i < n; i++)
    48     {
    49         os.width(12);
    50         os << fe[i];
    51         os.width(15);
    52         os << int(fo / fe[i] + 0.5) << endl;
    53     }
    54     os.setf(initial);  // restore initial formatting state
    55 }

      file_it (fout, objective, eps, LIMIT);  // 将目镜数据写入到文件 ep-data.txt 中

      file_it (cout, objective, eps, LIMIT);      // 将目镜数据显示到屏幕上   

       程序演示了如何使用 ostream 类中的格式化方法:

        方法 setf() 能够设置各种格式化状态:

          setf (ios_base::fixed) 将对象置于使用定点表示法的模式;

          setf (ios_base::showpoint) 将对象置于显示小数点的模式

        方法 precision() 指定此案时多少位小数(假定对象处于定点模式下);

        方法 width() 设置下一次输出操作使用的字符段宽度,只在显示下一个值时有效,然后将恢复默认设置。

       每个对象都存储了自己的格式化设置,因此,当程序将 cout 或者 fout 传递给 file_it 时,先修改格式化设置再恢复

    默认参数:

       默认参数指的是当函数调用中省略了实参时自动使用的一个值:

        char * left ( const char * str,int n = 1 ):

          默认参数值是初始化值,因此原型将 n 初始化为 1,调用函数 left 如果省略参数 n,则它的值为1;

          如果没有省略参数 n 的传递,则传递值将覆盖默认参数值

       对于带参数列表的函数,必须从右向左添加默认值;

       实参按从左到右的顺序依次被赋给相应的形参;

       通过使用默认参数可以减少要定义的析构函数、方法以及方法重载的数量

    函数重载:

       函数多态(函数重载),让我们可以使用多个同名的函数:

        可以通过函数重载来设计一系列函数——完成相同的工作,但使用不同的参数列表;

       函数重载的关键是函数的参数列表——也称为函数特征标

       需要注意,是特征标而不是函数类型使得可以对函数进行重载

       函数重载例程:

     1 #include<iostream>
     2 using namespace std;
     3 unsigned long left(unsigned long num, unsigned ct);   // 处理整数
     4 char * left(const char * str, int n = 1);        // 处理字符串
     5 
     6 int main()
     7 {
     8     char * trip = "Hawaii!! ";
     9     unsigned long n = 12345678;
    10     int i;
    11     char *temp;
    12     for (i = 1; i < 10; i++)
    13     {
    14         cout << left(n, i) << endl;
    15         temp = left(trip, i);
    16         cout << temp << endl;
    17         delete[] temp;
    18     }
    19     return 0;
    20 }
    21 unsigned long left(unsigned long num, unsigned ct)
    22 {
    23     unsigned digits = 1;
    24     unsigned long n = num;
    25 
    26     if (ct == 0 || num == 0)
    27         return 0;
    28     while (n /= 10)
    29         digits++;
    30     if (digits > ct)
    31     {
    32         ct = digits - ct;
    33         while (ct--)
    34             num /= 10;
    35         return num;
    36     }
    37     else
    38         return num;
    39 }
    40 
    41 char * left(const char * str, int n)
    42 {
    43     if (n < 0)
    44         n = 0;
    45     char *p = new char[n + 1];
    46     int i;
    47     for (i = 0; i < n && str[i]; i++)
    48         p[i] = str[i];
    49     while (i <= n)
    50         p[i++] = '';
    51     return p;
    52     
    53 }

     何时使用函数重载:

       仅当函数基本上执行相同的任务,但不使用不同形式的数据时,才应采用函数重载

     函数模板:

       函数模板是通用函数描述——使用通用类型来定义函数;

       通过将类型作为参数传递给模板,可以使编译器生成该类型的函数;

       建立交换模板:

        template <class Any>                     // template <typename Any>

        void Swap (Any &a,Any &b)

        {

          Any temp;

          temp = a;

          a = b;

          b = temp;

        }

     重载的模板:

       并非所有的类型都使用相同的算法,可以像重载常规函数定义那样重载模板定义;

       被重载的模板的函数特征标必须不同;

        template <class Any>

        void Swap (Any &a,Any &b);

     

        template <class Any>

        void Swap (Any *a,Any *b,int n);

     显示具体化:

       可以提供一个具体化函数定义——称为显示具体化;

       当编译器找到与函数调用匹配的具体化定义时,将使用该定义,而不再寻找模板;

       C++标准选择的具体化方法:

        1. 对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及他们的重载版本;

        2. 显示具体化的原型和定义应以 template<> 打头,并通过名称来指出类型;

        3. 具体化将覆盖常规模板,而非模板函数将覆盖具体化的原型

         void Swap (job &,job &);                                // 非模板函数原型

        template <class Any>

        void Swap (Any &,Any &);                              // 模板函数原型

        template <> void Swap<job> (job &,job &);    // 具体化原型

     实例化和具体化:

       模板并非函数定义,编译器使用模板为特定类型生成函数定义时,得到的是模板实例:

        函数调用 Swap(i,j) 导致编译器生成一个 Swap() 的一个实例——隐式实例

        还可以直接命令编译器创建特定的实例,如 Swap<int>() ——显示化实例

          temeplate void Swap<int> (int,int);

       显示具体化使用下面两个等价声明之一:

        template <> void Swap <int> (int &,int &);

        template <> void Swap ( int &,int &);

       

       

  • 相关阅读:
    路由器DHCP 动态主机配置
    nat转换
    静态路由的实验
    不同vlan之间的相互访问
    IP的包头格式什么?请分析每个字段的含义
    IP地址的分类
    关于对JSON.parse()与JSON.stringify()的理解
    Ajax工作原理
    关于css伪类
    开发 | 如何在微信小程序的页面间传递数据?
  • 原文地址:https://www.cnblogs.com/kidycharon/p/9705316.html
Copyright © 2020-2023  润新知