C++(7-8章)笔记

 ̄綄美尐妖づ 提交于 2020-02-21 02:55:18

第七章 函数——C++的编程模块

7.1函数
   1,函数如何返回值的?
      答:函数通过将返回值复制到指定的cpu寄存器或内存单元中来将其返回。随后,调用程序将查看该内存单元。返回函数和调用函数必须就该内存单元中存储的数据的类型达成一致。函数原型将返回值类型告知调用程序,而函数定义命令被调用函数应返回什么类型的数据。
  2,为什么需要原型?
      答:原型描述了函数到编译器的接口,也就是说,它将函数返回值的类型(如果有的话)以及参数的类型和数量告诉编译器。
  3,如果该函数不能修改形参中的数组该如何操作〉
   答:可以如下:void show_array(cons tint arr[],int n);
           在加上const之后表示该数组无法再sho_array()被修改,但并不是表示该数组中的所有数都是常量。
        C++将const  int  arr[]解释为const int *arr。因此该声明实际上是说,arr指向的是一个常量值。

 

第八章 函数探幽

8.1 C++内联函数
 1,常规函数和内联函数之间的主要区别不在于编写方式,而是在于C++编译器如何将他们组合到程序中。
 2,函数执行时内部的操作方式:
    编译器执行到函数调用的指令时,程序将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈(为此保留的内存块),跳到标记函数起点的内存单元,执行函数代码(也许还需将返回值放入到寄存器中),然后跳回到地址被保存你的指令处。
   C++内联函数提供了另一种选择。内联函数的编译代码与其他程序“内联”起来。也就是说,编译器将使用相应的函数代码替换函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,然后再跳回来。因此,内联函数执行速度比常规函数稍快,但代价就是消耗更多的内存。如果程序在10个不同的地方调用了同一个内联函数,那程序将包括该函数的10个代码拷贝。
  使用:在函数声明前加上关键字inline。函数定义前加上关键字inline。 内联函数不能递归。
8.2 引用变量
  1,int rats = 100; int & rod = rats;
   这表示rod引用了rats,这两个变量的地址是相同的。
   与指针有所不同在于,指针可以先声明,再赋值。这个必须在初始化时就赋值。 且 rod只能被赋值一次。 引用通常用于函数里面。Rod = x+3; 这是错误的。
  2,如果函数调用的参数不是左值或与相应的const引用参数的类型不匹配,则C++将创建类型正确的匿名变量,将函数调用的参数的值传递给该匿名变量,并让参数来引用该变量。
  3,应尽可能的使用const。
8.2.4将引用用于结构。P233页
  定义函数: const sysop & use(sysop &sysopref);
  1,第一个调用函数演示:ues(looper); 该函数调用将结构体looper按引用传递给use()函数,使得sysopref成为了looper的别名。
  2,第二个是将引用作为返回值,通常返回机制将返回值复制到临时存储区域中,随后调用程序将访问该区域。然而,返回引用意味着调用程序将直接访问返回值,而不需要拷贝。通常,引用将指向传递给函数的引用,因此调用函数实际上是直接访问自己的一个变量。例如,在这个例子中,sysopref是looper的引用,因此返回值是main()中的原始looper变量。请看下面一行代码:
   copycat = use(looper);
   如果函数use()返回一个结构,sysopref的内容将被复制到一个临时返回存储单元中,返回该临时返回存储单元的内容将被复制到copycast中。然而,由于use()返回一个指向looper的引用,在这种情况下,looper的内存将被直接复制到copycast中。这使得执行效率更高。
记住:返回引用的函数实际上是被引用的变量的别名。
   3,该程序使用函数调用来访问结构的成员
    cout << “ues(looper):”<<use(looper).used<<endl;
   由于函数use()返回一个指向looper的引用,因此上述代码与下面等价:
      use(looper);cout<<”use(looper)”<<looper.used<<endl;
     表示法use(looper).used访问looper的成员used。如果该函数返回一个结构而不是指向结构的引用,则这些代码访问的将是looper的临时拷贝的used成员。
 2,返回引用时需要主要的问题
   1,应避免返回当函数终止时不再存在的内存单元引用。
   2,可采用new来分配新的存储空间。
3,为何将const用于引用返回类型
  1,它并不意味着结构体sysop本身为const,而只一位着你不能使用返回的引用来直接修改它指向的结构。如省掉const就有可能出现下面的代码:

  Use(looper).used = 10;这会使设计中添加模糊特性。增加犯错机会。
8.3默认参数
  1,在函数调用时,可省掉实参,从而省掉的形参采用默认值
  2,在原型中声明:
    char *left(const char *str, int n = 1);
  3,必须从右向左添加默认值,也就是说,要为某个参数设置默认值,则必须为它右边的所有参数提供默认值。
8.4函数重载
  1,必须参数数目或参数类型不同。
  2,名称修饰:
      C++如何跟踪每一个重载函数呢?它给这些函数指定了秘密身份。使用C++开发工具中的编译器编写和编译程序时,C++编译器将执行一些神奇的操作——名称修饰或名称矫正。他根据函数原型中指定的形参类型对每个函数名进行加密。如:
  long MyFunctionFoo(int,float)。将被名称转换为不太好看的内部表示,来描述该接口,如下所示: ?MyFunctionFoo@@YAXH@Z
对原始名称进行的表面看来无意义的修饰将对参数数目和类型进行编码。
8.5函数模板
   1,函数模板就是使用通用类型(可用具体类型int,float替换)来定义函数。  
   2,函数模板允许以仁义类型的方式来定义函数,例如:
   template <class Any>
   void Swap(Any &a,Any &b)
    {
       Any tem; temp = a; a = b; b = temp;
    }
   第一行指出要建立一个模板,并将类型令名为Any。关键字template和class是必须的。除非可以使用关键字typename代替class。另外必须使用尖括号。类型名可以任意选择(这里为Any),只要遵守C++令名规则即可;模板并不创建任何函数,而只是告诉编译器如何定义函数。需要交换int的函数时,编译器将按模板模式创建这样的函数,并用int代替Any。
 提示:如果需要多个将同一种算法用于不同类型的函数,请使用模板,如果不考虑向后兼容的问题,并愿意键入较长的单词,则声明类型参数时,应使用关键字typename而不使用class。
 注意:函数模板不能缩短可执行程序,最终的代码不包括任何模板,而只包括了为程序生成的实际函数。模板的好处在于:生成多个函数定义更简单,更可靠。

8.5.1重载函数模板
   1,如果我们定义了Swap的模板,可现在要对两个数组进行交换,我们又应该怎么操作呢?
   我们可以重载他,定义void Swap(Any *a,Any *b,int n);这与重载函数有异曲同工之处。
8.5.2显式具体化
   如果我们想当编译器找到与函数调用匹配的具体话定义时,将使用该定义,而不再寻找模板。
   1,对于给定的函数名,可以有非模板函数,模板函数和显式具体化模板函数以及他们的重载版本。
   显式具体化的原型和定义应该以template<>打头,并通过名称来指出类型。
   具体化将覆盖常规模板,而非模板函数将覆盖具体化和常规模板。
   下面为交换job结构的非模板函数,模板函数和具体化的原型
    void Swap(job &,job &);///非模板
  
    template<class Any> ///模板
    void Swap(Any &,Any &);

    template<>void Swap<job>(job &,job &);///显式具体化
    template<>void Swap(job &,job &);///simpler form 同显式具体化
 8.5.3实例化和具体化
   1,语法:template void Swap<int>(int,int);
      实现了这种特性的编译器看到上述声明后,将使用Swap()模板生成一个使用int类型的实例。也就是说,该声明的意思是“使用Swap()模板生成int类型的函数定义”。(函数会有一个Swap函数)
   与显式实例化不同的是,显式具体化使用下面两个等价的声明之一:
   template<>void Swap<int>(int &,int&);
   template <>void Swap(int &,int &);

(显式具体化只会在程序中如果有调用到此显式才会定义这个函数)
注意:试图在同一个编程单元中使用同一种类型的显式实例和显式具体化将出错。
   隐式实例化,显式实例化和显式具体化统称为具体化。他们的相同之处在于,他们表示的都是使用具体类型的函数定义,而不是通用描述。
8.5.4编译器选择使用哪个函数版本
  1,编译器决定使用哪个函数,这个过程称为重载解析,
    第一步:创建候选函数列表。其中包含与被调用函数的名称相同的函数和模板函数。
    第二步:使用候选函数列表创建可行函数列表。这些都是参数数目正确的函数,为此有一个隐式转换序列,其中包括实参类型与相应的形参类型完全匹配的情况。例如,使用float参数的函数调用可以将该参数转换为double,从而与double形参匹配,而模板可以为float生成一个实例。
     第三步:确定是否有最佳的可行函数。如果有,则使用它,否则该函数调用出错。
      如何确定最佳:(最佳到最差顺序如下)
      1,完全匹配,但常规函数优先于模板
      2,提升转换(char转int)
      3,标准转换(int转char)
      4,用户定义的转换,如类声明中定义的转换。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!