CPrimerPlus学习笔记之函数

前言

个人觉得学习编程最有效的方法是阅读专业的书籍,通过阅读专业书籍可以构建更加系统化的知识体系。一直以来都很想深入学习一下C++,将其作为自己的主力开发语言。现在为了完成自己这一直以来的心愿,准备认真学习《C++PrimerPlus》。为了提高学习效率,在学习的过程中将通过发布学习笔记的方式,持续记录自己学习C++的过程。

一、C++内联函数

内联函数是C++为提高程序运行速度所做的一项改进。常规函数和内联函数之间的主要区别不在于编写方式,而在于C++编译器如何将它们组合到程序中。

对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存。如果程序在10个不同的地方调用同一个内联函数,则该程序将包含该函数代码的10个副本。

要使用这项特性,必须采取下述措施之一:

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

在函数定义前加上关键字inline。通常的做法是省略原型,将整个定义(即函数头和所有函数代码)放在本应提供原型的地方

二、引用变量

C++新增了一种复合类型——引用变量。引用是已定义的变量的别名(另一个名称)。

C和C++使用符号来指示变量的地址。C++给符号赋予了另一个含义,将其用来声明引用。例如,要将rodents作为rats变量的别名,可以这样做:

intrats;introdents=rats;

int指的是指向int的引用。

引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。这种传递参数的方法称为按引用传递。按引用传递允许被调用的函数能够访问调用函数中的变量。

如果意图是让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应使用常量引用:

doublerefcube(constdoublera);

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

如果引用参数是const,则编译器将在下面两种情况下生成临时变量:

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

实参的类型不正确,但可以转换为正确的类型。

**左值参数:**是可被引用的数据对象,例如,变量、数组元素、结构成员、引用和解除引用的指针都是左值。

**非左值:**包括字面常量(用引号括起的字符串除外,它们由其地址表示)和包含多项的表达式。在C语言中,左值最初指的是可出现在赋值语句左边的实体,但这是引入关键字const之前的况。现在,常规变量和const变量都可视为左值,因为可通过地址访问它们。但常规变量属于可修改的左值,而const变量属于不可修改的左值。

将引用参教声明为常量数据的引用的理由有三个:

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

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

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

C++11新增了另一种引用——右值引用(rvaluereference)。这种引用可指向右值,是使用声明的:

doublerref=std::sqrt(6.00);

新增右值引用的主要目的是,让库设计人员能够提供有些操作的更有效实现。

引用非常适合用于结构和类(C++的用户定义类型)。

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

返回引用时最重要的一点是,应避免返回函数终止时不再存在的内存单元引用。为避免这种问题,最简单的方法是,返回一个作为参数传递给函数的引用。另一种方法是用new来分配新的存储空间。

使得能够将特性从一个类传递给另一个类的语言特性被称为继承。

继承的另一个特征是,基类引用可以指向派生类对象,而无需进行强制类型转换。

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

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

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

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

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

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

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

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

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

如果数据对象是内置数据类型,则使用指针。如果看到诸如txi(x)这样的代码(其中×尼imnt)则很明显,该函数将修改x。

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

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

如果数据对象是类对象,则使用引用。当然,这只是一些指导原则,很可能有充分的理由做出其他的选择。

三、默认参数

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

char*left(constchar*str,intn=1);

对于带参数列表的函数,必须从右向左添加默认值。也就是说,要为某个参数设置默认值,则必须为它右边的所有参数提供默认值。

四、函数重载

函数多态是C++在C语言的基础上新增的功能。术语“多态”指的是有多种形式,因此函数多态允许函数可以有多种形式。类似地,术语“函数重载"’指的是可以有多个同名的函数,因此对名称进行了重载。这两个术语指的是同一回事,但我们通常使用函数重载。

函数重载的关键是函数的参数列表——也称为函数特征标(functionsignature)。编译器在检查函数特征标时,将把类型引用和类型本身视为同一个特征标。

五、函数模板

函数模板是通用的函数描述,也就是说,它们使用泛型来定义函数,其中的泛型可用具体地类型(如int或double)替换。由于类型是用参数表示的,因此模板特性有时也被称为参数化类型。

templatetypenameAnyTypevoidSwap(AnyTypea,AnyTypeb){AnyTypetemp;temp=a;a=b;b=temp;}

关键字template和typename是必需的,除非可用使用关键字class代替typename。另外,必须使用尖括号。

在标准C++98添加关键字typename之前,C++使用关键字class来创建模板。也就是说,可以这样编写模板定义:

templateclassAnyTypevoidSwap(AnyTypea,AnyTypeb){AnyTypetemp;temp=a;a=b;b=temp;}

可以提供一个具体化函数定义——称为显式具体化(explicitspecialization),其中包含所需的代码。

C++98标准选择了下面的具体化方法:

对于给定的函数名,可以有非模板函数、模板函数和显式具体化模板函数以及它们的重载版本。

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

具体化优先于常规模板,而非模板函数优先于具体化和常规模板。

templatevoidSwap(job,job);

为进一步了解模板,必须理解术语实例化和具体化。记住,在代码中包合函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成明数定义时,得到的是模板实例(instantiation)。这种实例化方式被称为隐式实例化(implicitinstantiation)。

现在C++还允许显式实例化(explicitinstantiation):

templatevoidSwapint(int,int);

与显式实例化不同的是,显式具体化使用下面两个等价的声明之一:

templatevoidSwapint(int,int);templatevoidSwap(int,int);

区别在于,这些声明的意思是“不要使用Swap()模板来生成函数定义,而应使用专门为int类型显式地定义的函数定义”。这些原型必须有自己的函数定义。

显式具体化声明在关键字template后包含,而显式实例化没有。

完全匹配允许的无关紧要的转换:

指向非const数据的指针和引用]优先与非const指针和引用参数匹配。

其中一个是非模板函数,而另一个不是。在这种情况下,非模板函数将优先于模板函致(包括显式具体化)。

如果两个完全匹配的函数都是模板函数,则较具体的模板函数优先。

templateclassT1,classT2voidft(T1x,T2y){?type?xpy=x+y;}

以上代码中xpy的类型无法明确,可以通过C++11新增关键字decltype来解决:

templateclassT1,classT2voidft(T1x,T2y){decltype(x+y)xpy=x+y;}

templateclassT1,classT2?type?ft(T1x,T2y){returnx+y;}

以上代码中返回类型无法明确,可以通过auto关键字和关键字decltype组合使用来解决:

templateclassT1,classT2autoft(T1x,T2y)-decltype(x+y){returnx+y;}




转载请注明:http://www.aierlanlan.com/tzrz/7516.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了