c++基础知识(6)
c++ primer plus 第八章,包括引用变量&、默认参数、函数重载、函数模板。
c++内联函数
内联函数是c++为提高程序运行速度所做的一项改进。常规函数和内联函数的主要区别不在于编写方式,而在于c++编译器如何将它们组合到程序中。
编译过程的最终产品是可执行程序——由–组机器语言指令组成。运行程序时,操作系统将这些指令载入到计算机内存中,因此每条指令都有特定的内存地址。计算机随后将逐步执行这些指令。有时(如有循环或分支语句时),将跳过–些指令,向前或向后跳到特定地址。常规函数调用也使程序跳到另一个地址;(函数的地址),并在函数结束时返回。来回跳跃并记录跳跃位置意味着以前使用函数时,需要一定的开销。
内联函数的编译代码与其他程序代码“内联”起来了。即编译器将使用相应的函数代码替代函数调用。对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此内联函数的运行速度比常规函数更快,但代价是需要占用更多内存。
常规函数:跳转;内联函数:复制
代码执行时间>函数调用时间–>使用常规函数
代码执行时间<函数调用时间–>使用内联函数
要使用内联函数,必须采用以下措施之一
- 在函数声明前加上关键字
inline
- 在函数定义前加上关键字
inline
内联函数也是按值传递
引用变量
引用是已定义的变量的别名(另一个名称)(外号)
主要用途是用作函数的形参。通过将引用变量用作参数,函数将使用原始数据,而不是其副本。
除了指针以外,引用也为函数处理大型结构提供了一种非常方便的途径。
创建引用变量
使用&符号来声明引用
1 |
|
必须在声明引用时进行初始化,一旦与某个变量关联起来,就将一直效忠于它。
将引用用作函数参数
引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。这种传递参数的方法称为按引用传递。按引用传递允许被调用的函数能够方位调用函数中的变量。(操作形参=操作实参)
1 |
|
引用的属性和特别之处
如果想要让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应使用常量引用(通过const从而无法修改引用的值)
例如 double refcube (const double &ra)
1 |
|
对于按值传递的函数,例如上述代码中的cube
函数,可使用多种类型的实参,例如以下调用都是合法的
1 |
|
但是将上述类似的参数传递给接收引用参数的函数,会发现出错,这是因为传递引用的限制更严格,
–>临时变量、引用参数和const
当参数为const引用时,如果实参与引用参数不匹配,c++将生成临时变量
将引用参数声明为常量数据的引用的理由有
- 使用const可以避免无意中修改数据的编程错误
- 使用const使函数能够处理const和非const实参,否则将只能接受非const数据
- 使用const引用使函数能够正确生成并使用临时变量
将引用用于结构体
假设有如下结构体定义:
1 |
|
为什么要返回引用?不需要将值复制到临时变量,效率更高
返回引用时,应避免返回函数终止时不再存在的内存单元引用(函数局部变量的引用)
何时使用引用参数
默认参数
默认参数指的是当函数调用中省略了实参时自动使用的一个值
必须通过函数原型来设置默认值,由于编译器通过查看函数原型来了解函数所使用的参数数目,因此函数原型也必须将可能得默认参数告知程序。方法是将值赋给原型中的参数。
1 |
|
对于多个参数的函数,必须从右向左添加默认值。也就是说,要为某个参数设置默认值,则必须为它右边的所有参数提供默认值:
1 |
|
实参按从左到右的顺序依次被赋给相应的形参,而不能跳过任何参数,例如下面的调用是不允许的
1 |
|
函数重载
函数多态(函数重载)指的是可以有多个同名的函数。可以通过函数重载来设计一系列函数,这些函数完成相似的工作,但是使用不同的参数列表。
1 |
|
仅当函数基本上执行相同的任务,但使用不同的数据时,才应采用函数重载。
函数模板
需要对多个不同类型的数据使用同一种算法时,可以使用模板。
函数模板是通用的函数描述,也就是说,它们可以使用泛型来定义函数,其中的泛型可用具体的类型(如int或double替换)。
例如已经定义了一个交换两个int值的函数,假设现在要交换两个double值,那么一种方法是复制原来的代码,用double替换所有的int。如果需要交换两个char值,可以再次使用同样的技术。然而进行这种修改会浪费时间且容易出错。
c++的函数模板能够自动完成这一功能,可以节省时间,而且更可靠
定义
1 |
|
例如,以下是一个交换函数模板
1 |
|
模板并不创建任何函数,而只是告诉编译器如何定义函数。需要交换int的函数时,编译器将按模板创建这样的函数,并用int代替AnyType。同样,需要交换double的函数时,编译器将按模板创建这样的函数,并用double代替AnyType。
模板的局限性
假设有如下模板
1 |
|
假设function f
中定义了复制a = b
,但是如果a
和b
是数组,那么这种运算将不成立
在使用函数模板时,编写的函数模板可能无法处理某些类型。
1 |
|
假设只想交换salary
和floor
成员,而不交换name
成员,则无法通过模板重载来提供其他的代码。
然而,可以提供一个具体化函数定义——称为显式具体化
其中包含所需的代码。当编译器找到与函数调用匹配的具体化定义时,将使用该定义,而不再寻找模板。
显式具体化
- 对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及他们的重载版本
- 显式具体化的原型和定义应以
template<>
打头,并通过名称来指出类型 - 具体化优先于常规模板,而非模板函数优先于具体化和常规模板
1 |
|
实例化和具体化
实例化:
隐式实例化:编译器使用模板为特定类型生成函数定义时,得到的是模板实例
显式实例化:直接命令编译器创建特定的实例
Swap函数声明:
template void Swap<int>(int, int)
,意思是使用Swap()模板生成int类型的函数定义。
显式具体化:template<> void Swap<int>(int &, int &);
意思是不要使用通用Swap模板来生成函数定义,而应使用专门为int类型显式的定义的函数定义。(专门定制)
显示具体化声明在关键字template后包含<>,而显式实例化没有。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!