函数探幽

函数探幽

  • 内联函数
    • c++为提高程序运行速度的一项改进。
    • 非内联函数在调用时来回跳跃并记录产生了一定的开销。
    • 内联函数编译器将使用相应的函数代码替换函数调用。无需跳到另一个位置去执行代码。
    • 内联函数运行速度比常规函数稍快,但代价是占用更多内存。
    • 如果程序在10个不同的地方调用同一个内联函数,该程序将包含该函数代码的10个副本。
    • 如果执行函数代码时间比函数调用时间短,则应该使用内联函数。
    • 要使用内联函数需要在函数声明和定义前加上inline关键字。
    • 通常的做法是省略声明,直接定义。
    • 有些函数即使声明了inline编译器也不一定会将其作为内联函数,可能的原因有函数过大或函数调用了自己。
    • 有些编译器没有实现内联函数。
    • 内联与宏
      • 内联代码的原始实现是宏
      • 宏不能按值传递
      • #define SQUARE(X) X*X
      • int c = 13
      • a = SQUARE(c++)
      • 则替换后的结果为a = c++*c++;
      • 将c递增了两次。
      • 但使用内联函数就只会递增一次。
      • 所以推荐使用内联函数。
  • 引用变量
    • 引用是一定义的变量的别名。
    • 引用变量的主要用途是用作函数的形参。
    • 通过使用引用变量作为参数,函数将使用原始数据,而不是副本。
    • 创建引用变量
      • &符号
      • int rats;
      • int & rodents = rats;
      • int &的意思是指向int的引用。
      • rodents和rats指向相同的值和内存单元。
      • 注意区别地址运算符和引用
    • 指针和引用的区别之一
      • 引用在声明是必须初始化,而指针不需要。
      • 引用更接近const指针。一旦与某个变量关联起来,就将一直效忠与他。类似于int * const pr = &rats;
      • 只能通过初始化声明来设置引用,但不能通过赋值来设置。
    • 引用变量的特别之处
      • 如果只是想使用变量中的数据,那么引用参数应该使用常量引用。如const double & a;
      • 在参数数据比较大时,引用参数很有用。
      • 按值传递的参数可使用多种类型的实参。
      • double z = cube(x+2.0);
      • z = cube(8.0);
      • int k = 10;
      • z = cube(k);
      • double yo[3] = {2.2, 3.3, 4.4};
      • z = cube(yo[2]);
      • 上述参数传递给接受引用参数的函数时,现代c++中这是错误的。在较老的编译器中会发出警告。
      • 早期c++允许将表达式传递给引用变量。
      • 具体方式是创建应该临时变量。
      • 临时变量
        • 如果实参与引用参数不匹配。c++将生成临时变量。
        • 现在只有参数为const引用时c++才允许,但以前不是。
        • 实参类型正确,但不是左值。非左值有表达式,字符串常量等。
        • 实参类型不正确,但可以转换为正确类型。
        • 现在常规变量和const变量都可被视为左值。因为可以通过地址访问他们。
        • 这些临时变量只在函数调用期间存在,此后编译器将随意将其删除。
        • 在现在的大多数c++编译器这种行为只对于常量引用可行。
        • 在早期c++较宽松时可以接受不是常量引用而创建临时变量,但达不到修改引用的目的。
  • 尽可能多的使用const
    • 1 使用const可以避免无意中修改数据的错误。
    • 2 const可使能够处理const和非const实参,否则只能接受非const数据。
    • 3 const引用使函数能够正确生成并使用临时变量。
  • c++11新增了另一种引用——右值引用
    • 使用&&声明
    • 使用右值引用实现移动语义。后面讲。
  • 为什么要返回引用
    • 如果不是返回引用,那么函数将返回结果复制到应该临时位置,再将临时位置中的内存拷贝到dup中。但返回值为引用时,直接把team复制到dup中。省去临时位置的步骤。
  • 避免返回指向临时变量的指针和引用。因为函数结束后临时变量将不存在。
    • 两种方法避免
    • 返回一个作为参数传递给函数的引用。
    • new来分配新的存储空间。
    • 但new来分配会存在一个释放内存的问题。auto_ptr 模板和c++11新增的unique_ptr可帮助程序员自动完成释放工作。16章介绍。
  • 假设要使用一个函数返回的引用,而不是给返回的引用赋值。那么应该将返回的引用声明为const引用。

  • 对象继承和引用
    • 基类引用可以指向派生类对象,无需进行强制类型转换。
  • 何时使用引用参数。
    • 对于使用传递的值而不修改的函数
      • 如果数据对象很小,如内置的数据类型或小型结构,则按值传递。
      • 如果数据对象是数组,则使用指针,并将指针声明为const指针。
      • 如果数据对象比较大,则使用const指针或const引用,提高效率。
      • 如果数据对象是类对象,则使用const引用。传递类对象的标准方式是按引用传递。
    • 对于修改调用函数中数据的函数
      • 如果数据对象是内置数据类型,则使用指针。
      • 如果数据对象是数组,则使用指针。
      • 如果数据对象是结构,使用指针或引用。
      • 如果数据对象是类,则使用引用。
  • 默认参数
    • 如何设置默认参数。
    • char * left(const char * str, int n = 1);
    • 为某个参数设置默认值,则必须为它右边的所有参数提供默认值。
    • 只有原型也就是函数声明指定了默认值,函数定义与没有默认参数时完全相同。
  • 函数重载
    • 可以通过函数重载来设计一系列函数,他们完成相同的工作,但使用不同的参数列表。
    • 如果参数数目不同或参数类型不同,则特征标也不同。但是一些看起来彼此不同的特征标是不能共存的。
    • 在函数调用时,将调用与函数最匹配的那一个。如果最匹配的没有,那么调用第二匹配的。以此类推。
  • 名称修饰(名称矫正)
    • 它根据函数原型中指定的形参对每个函数名进行加密。
    • 编译器将名称转换为不太好看的内部表示。
    • 对参数数目和类型进行编码。添加一组符号随特征标而异。
  • 函数模板
    • 函数模板是通用函数的描述。
    • 他们使用泛型来定义函数。
    template <typename T>//也可以使用template <class T>
    void Swap(T &a, T &b)
    {
      T = temp;
      temp = a;
      a = b;
      b = temp;
    }
  • 为特定类型提供具体化的模板定义。
  • 显示具体化不会根据模板生成函数定义,而是使用对特定类型定义的专用函数定义。
    • template<> void Swap(job &j1, job &j2);
  • 实例化
    • 代码中包含函数模板本身并不会生成函数定义。只是一个生成函数定义的方案。
    • 编译器为特定类型生成函数定义时得到的是模板实例。这种实例化方法被称为隐式实例化。例如直接调用Swap(a, b);
    • 最初编译器只能通过隐式实例化,来生使用模板生成函数定义。
    • 现在c++还允许显示实例化。
    • template void Swap< int >(int, int);
    • 还可以在函数中创建显示实例化
    • Swap< double >(a, b);
  • 编译器使用哪个函数版本
    • 第一步:创建候选列表。其中包含与被调函数的名称相同的函数和模板函数。
    • 第二步:使用候选列表创建可行函数列表。
    • 第三步:检测是否有最佳的可行函数。如果有使用它,如果没有函数调用出错。

相关推荐