C++ Prime Plus学习笔记:第八章 函数探幽

第八章 函数探幽

8.1. C++内联函数

内联函数是CPP为了提高程序运行速度所做出的一项改进。常规函数和内联函数之间的主要区别不在于编写方式,而在于CPP编译器如何将他们组合到程序当中。什么意思呢,就是对程序员除了关键字以外,没有任何编写代码方面的区别,它是让编译器来识别的,不同函数编译器处理的方式不同。

内联函数的编译代码与其他程序代码“内联”起来了。也就是说,编译器将使用相应的函数代码替换函数调用。对于内联代码,程序无需跳到另一个位置来执行代码(详情见计算机组成原理)。因此,内联函数的运行速度比常规函数稍快,但是代价是会占用更多内存。如果程序在10个不同的地方调用同一个内联函数,那么这个程序将会在内存中生成10个相同的这个函数的代码。

使用方法,二选一:

  1. 在函数声明前加上关键字inline
  2. 在函数定义前加上关键字inline

通常的做法是省略原型,将整个定义放在本应该提供原型的地方。这也说明了内联函数最好是那种比较短的程序。

内联与红

inline是CPP新增的特性。C语言使用#define来提供宏——内联代码的原始实现。

8.2. 引用变量

CPP新增了一种复合类型——引用变量。引用是已定义的变量的别名。比如,如果将twin作为element变量的引用,那么可以使用twin和element交替来使用这个变量。引用变量的主要作用是作为函数的形参。通过将引用变量用作参数,函数将使用原始数据,而不是其副本。这样的话,除了指针以外,引用也为函数处理大型结构提供了一种非常方便的途径,同时对于设计类来说,引用也是必不可少的。

int rats;
int& rodents = rats;

其中,&不是地址运算符,而是类型标识符的一部分。就想声明中的char*是指向char的指针一样,int&指向的是int的引用。上述声明允许rats和rodents互换——它们指向相同的值和内存单元(地址相同)。使用其中一个变量名称来改变内存当中的值,另一个变量名称也会改变。

它和指针的区别在哪呢?

因为改变它,那么另一个引用也会改变,所以这个引用更加像const,只能在初始化时进行赋值。而指针如果改变它指向的地址,另一个变量名称所指向的东西则不会改变。

8.2.2. 将引用用作函数参数

引用经常被用作函数参数,是的函数中的变量名成为调用程序中的变量的别名。这种传递参数的方法被称为按引用传递。这允许被调用的函数能够访问调用函数中的变量。C语言只能按值传递,这样会导致被调用函数使用调用程序的值的拷贝。当然,C语言还能使用指针传递的方式来实现上述的操作。

8.2.3. 引用的属性和特别之处

如果实参与引用参数不匹配,CPP将生成临时变量。当前,仅当参数为const引用时,CPP才允许这样做。

什么时候创建临时变量你额?

  1. 实参的类型正确,但不是左值
  2. 实参的类型不正确,但可以转换为正确的类型

如果接受引用参数的函数的意图是修改作为参数传递的变量,则创建临时变量将阻止这种意图的实现。解决方法是,禁止创建临时变量。

8.2.4. 将引用用于结构

引用非常适合用于结构和类,只需在声明结构参数时使用引用运算符&就可以。

struct free_throws{
    std::string name;
    int made;
    int attempts;
    float percent;
};

函数原型:

void set_pc(free_throws& ft);

// 如果不希望函数修改传入的结构,可使用const
void display(const free_throws& ft);

8.2.5. 将引用用于类对象

将类对象传递给函数时,CPP通常的做法是使用引用。

例如,可以通过使用引用,让函数将类string、ostream、istream、ofstream和ifstream等类的对象作为参数

8.2.7. 何时使用引用参数

使用引用参数的主要原因有两个。

• 程序员能够修改调用函数中的数据对象。

• 通过传递引用而不是整个数据对象, 可以提高程序的运行速度。

当数据对象较大时(如结构和类对象), 第二个原因最重要。这些也是使用指针参数的原因。这是有道理的, 因为引用参数实际上是基千指针的代码的另一个接口。那么, 什么时候应使用引用、什么时候应使用指针呢?什么时候应按值传递呢?下面是一些指导原则:对千使用传递的值而不作修改的函数。

• 如果数据对象很小, 如内置数据类型或小型结构, 则按值传递。

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

• 如果数据对象是较大的结构, 则使用const 指针或const 引用, 以提高程序的效率。这样可以节省复制结构所需的时间和空间。

• 如果数据对象是类对象, 则使用const 引用。类设计的语义常常要求使用引用, 这是C++新增这项特性的主要原因。因此, 传递类对象参数的标准方式是按引用传递。对于修改调用函数中数据的函数:

• 如果数据对象是内置数据类型,则使用指针。如果看到诸如fix.it (&x)这样的代码(其中x 是int),则很明显, 该函数将修改x。

• 如果数据对象是数组, 则只能使用指针。

• 如果数据对象是结构, 则使用引用或指针。

• 如果数据对象是类对象, 则使用引用。

当然, 这只是一些指导原则, 很可能有充分的理由做出其他的选择。例如, 对于基本类型, cin 使用引用, 因此可以使用cin>>n, 而不是cin >> &n。

8.3. 默认参数

默认参数指的是当函数调用中省略了实参时自动使用的一个值。例如, 如果将void wow (int n) 设置成n 有默认值为1’ 则函数调用wow()相当千wow (1)。这极大地提高了使用函数的灵活性。

如何设置默认值呢?必须通过函数原型。由千编译器通过查看原型来了解函数所使用的参数数目, 因此函数原型也必须将可能的默认参数告知程序。方法是将值赋给原型中的参数。例如, left( )的原型如下:

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

8.4. 函数重载

默认参数让你能够使用不同数目的参数调用同一个函数,而函数多态(函数重载)能让你使用多个同名的函数。“函数重载”指的是可以有多个同名的函数,因此对名称进行了重载。

多态和重载是一回事。

重载的关键是函数的参数列表,也称为函数特征标function signature。如果两个函数的参数数目和类型相同,同时参数的排列顺序也相同,则他们的特征标相同,而变量名是无关紧要的。

CPP如何跟踪每一个重载函数呢

通过名称修饰name decoration或者名称矫正name mangling,它根据函数原型中指定的形参类型对每个函数名进行加密。

long MyFunctionFoo(int, float);

?MyFunctionFoo@@YAXH

对参数数目和类型进行编码。添加的一组符号随函数特征标而不同,而修饰时使用的约定随编译器而异。

8.5.函数模板

函数模板是通用的函数描述,也就是说,它们使用泛型来定义函数。其中的泛型可以使用具体的类型来进行替换。通过将类型作为参数传递给模板,可以使编译器生成该类型的函数。由于模板允许以泛型的方式编写程序,因此也被成为通用编程。由于类型是用参数表示的,因此模板特性有时也被成为参数化类型。

template <typename AnyType>
void Swap(AnyType &a, AnyType &b){
    AnyType temp;
    temp = a;
    a = b;
    b = temp;
}

第一行指出,要建立一个模板,并将类型命名为AnyType。关键字template和typename是必需的,除非可以使用关键字class代替typename。另外,必需使用尖括号。类型名可以任意选择(这里为AnyType)。许多程序员都用简单的名称,比如T。剩下的代码描述了交换两个AnyType值的算法。模板并不创建任何函数,而是告诉编译器如何定义函数。需要交换int的函数时,编译器将模板模式创建这样的函数,并用int替代AnyType。同样,需要交换double的函数时,编译器将按模板模式创建这样的函数,并用double代替AnyType。

template <class AnyType>
void Swap(AnyType &a, AnyType &b){
    AnyType temp;
    temp = a;
    a = b;
    b = temp;
}

8.5.1. 重载的模板

需要多个对不同类型使用同一种算法的函数时,可以使用模板。然而,并非所有的类型都使用相同的算法。可以像重载常规函数定义那样重载模板定义。

8.5.5. 编译器选择使用哪一个版本

对于函数重载、函数模板、函数模板重载,CPP要决定为函数调用使用哪一个函数定义,尤其是有多个参数时。这个过程称为重载解析overloading resolution

  1. 创建候选函数列表。包含与被调用函数的名称相同的函数和模板函数
  2. 使用候选函数列表创建可行函数列表。这些都是参数数目正确的函数, 为此有一个隐式转换序列, 其中包括实参类型与相应的形参类型完全匹配的情况。例如, 使用float 参数的函数调用可以将该参数转换为double, 从而与double 形参匹配, 而模板可以为float生成一个实例。
  3. 确定是否有最佳的可行函数。如果有, 则使用它, 否则该函数调用出错。

还可以通过编写合适的函数调用,引导编译器作出你想要的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值