前言
个人觉得学习编程最有效的方法是阅读专业的书籍,通过阅读专业书籍可以构建更加系统化的知识体系。一直以来都很想深入学习一下C++,将其作为自己的主力开发语言。现在为了完成自己这一直以来的心愿,准备认真学习《C++PrimerPlus》。为了提高学习效率,在学习的过程中将通过发布学习笔记的方式,持续记录自己学习C++的过程。
一、复习函数的基本知识
要使用C++函数,必须完成如下工作:
提供函数定义;
提供函数原型;
调用函数。
1、定义函数
可以将函数分成两类:没有返回值的函数和有返回值的函数。
没有返回值的函数被称为void函数,其通用格式如下:
voidfunctionName(parameterList){statement(s)return;//可选}
其中,parameterList指定了传递给函数的参数类型和数量。
有返回值的函数将生成一个值,并将它返回给调用函数。通用格式如下:
typeNamefunctionName(parameterList){statementsreturnvalue;}
函数在执行返回语句后结束。如果函数包含多条返回语句(例如,它们位于不同的还ifelse选项中),则函数在执行遇到的第一条返回语句后结束。
2、函数原型和函数调用
原型描述了函数到编译器的接口、也就是说,它将函数返回值的类型(如果有的话)以及参数的类型和数量告诉编译器。
函数原型是一条语句,因此必须以分号结束。获得原型最简单的方法是,复制函数定义中的函数头,并添加分号。函数原型不要求提供变最名,有类型列表就足够了。
在C++中,括号为空与在括号中使用关键字void是等效的——意味着函数没有参数。
具体来说,原型确保以下几点:
编译器正确处理函数返回值;
编译器检查使用的参数数目是否正确;
编译器检查使用的参数类型是否正确;如果不正确,则转换为正确的类型(如果可能的话)
二、函数参数和按值传递
C++标准使用参数(argument)来表示实参,使用参量(parameter)来表示形参,因此参数传递将参数赋值给参量。
在函数中声明的变量(包括参数)是该函数私有的。在函数被调用时,计算机将为这些变量分配内存;在函数结束时,计算机将释放这些变量使用的内存(有些C++文献将分配和释放内存称为创建和毁坏变量)。这样的变量被称为局部变量,因为它们被限制在函数中。前面提到过,这样做有助于确保数据的完整性,这还意味着。如果在main()中声明了一个名为x的变量,同时在另一个函数中也声明了一个名为x的变量,则它们将是两个完全不同的、毫无关系的变量。这样的变量也被称为自动变量,因为它们是在程序执行过程中自动被分配和释放的。
函数可以有多个参数。在调用函数时,只需使用逗号将这些参数分开即可。
如果函数的两个参数的类型相同,则必须分别指定每个参数的类型,而不能像声明常规变量那样组合在一起。
三、函数和数组
在大多数情况下,C+和C语言一样,也将数组名视为指针。
在C++中,当且仅当用于函数头或函数原型中,int*arr和intarr[]的含义才是相同的。它们都意味着arr是一个int指针。然而,数组表示法(intarr[])提醒用户,arr不仅指向int,还指向int数组的第一个int。当指针指向数组的第一个元素时,本书使用数组表示法;而当指针指向一个独立的值时,使用指针表示法。别忘了,在其他的上下文中,int*arr和intarr[]的含义并不相同。
不能使用sizeof来获悉原始数组的长度,而必须依赖于传入正确的元素数。
为防止函数无意中修改数组的内容,可在声明形参时使用关键字const。
可以用两种不同方式将const关键字用于指针。第一种方法是让指针指向一个常量对象,这样可以防止使用该指针来修改所指向的值,第二种方法是将指针本身声明为常量,这样可以防止改变指针指向的位置。
声明一个指向常量的指针pt:
intage=39;constint*pt=age;
该声明指出,pt指向一个constint(这里为39),因此不能使用pt来修改这个值。换句话来说,*pt的值为const,不能被修改。这意味着pt的声明并不意味着它指向的值实际上就是一个常量,而只是意味着对pt而言,这个值是常量。
将const变量的地址赋给指向const的指针:
constfloatg_earth=9.80;constfloat*pe=g_earth;
五、函数和C风格字符串
假设要将字符串作为参数传递给函数,则表示字符串的方式有3种:
char数组;
用引号括起的字符串常量(也称字符串字面值);
被设置为字符串的地址的char指针。
可以说是将字符串作为参数来传递,但实际传递的是字符串第一个字符的地址。这意味着字符串函数原型应将其表示字符串的形参声明为char*类型
以下代码演示了处理字符串中字符的标准方式:
while(*str){statementsstr++;}
六、函数和结构
在涉及函数时,结构变量的行为更接近于基本的单值变量。可以按值传递结构,就像普通变量那样。函数可以返回结构。与数组名就是数组第一个元素的地址不同的是,结构名只是结构的名称,要获得结构的地址,必须使用地址运算符。
使用结构编程时,最直接的方式是像处理基本类型那样来处理结构;也就是说,将结构作为参数传递,并在需要时将结构用作返回值使用。然而,按值传递结构有一个缺点。如果结构非常大,则复制结构将增加内存要求,降低系统运行的速度。出于这些原因(同时由于最初C语言不允许按值传递结构),许多C程序员倾向于传递结构的地址,然后使用指针来访问结构的内容。C++提供了第三种选择按引用传递。
七、函数和string对象
虽然C风格字符串和string对象的用途几乎相同,但与数组相比,string对象与结构更相似。例如,可以将一个结构赋给另一个结构,也可以将一个对象赋给另一个对象。可以将结构作为完整的实体传递给函数,也可以将对象作为完整的实体进行传递。如果需要多个字符串,可以声明一个string对象数组,而不是二维char数组。
使用string对象需要包含string头文件。
如果需要string数组,只需使用通常的数组声明格式即可:
stringlist[SIZE];
可以像下面这样使用它:
getline(cin,list);
八、函数与array对象
在C++中,类对象是基于结构的,因此结构编程方面的有些考虑因素也适用于类。
使用C++11模板类array的例子。
假设您要使用一个array对象来存储一年四个季度的开支:
std::arraydouble,4expenses;
本书前面说过,要使用array类,需要包含头文件array,而名称array位于名称空间std中。如果想用函数来显示expenses的内容,可按值传递expenses:
show(expenses);
但如果函数要修改对象expenses,则需将该对象的地址传递给函数:
fill(expenses);
九、递归
C++函数除main()函数不能自己调用自己,其余函数可以自己调用自己,这种功能被称为递归。
十、函数指针
与数据项相似,函数也有地址。函数的地址是存储其机器语言代码的内存的开始地址。
要将函数地址传递给第一个函数,需要完成下面的工作:
获取函数的地址(只要使用函数名,后面不跟参数即可);
声明一个函数指针;
使用函数指针来调用函数。
当我们需要一个返回类型为double同时有一个int类型参数的函数作为参数时,需要进行如下声明:
voidMyCalcSquare(double(*pf)(int));
使用函数指针示例代码如下:
voidMyCalcSquare(intnum,double(*pf)(int));intmain(){usingnamespacestd;intcode=1;coutMyCalcSquare(code,DealInt)endl;}doubleDealInt(intnum){return0.03*lns;}voidMyCalcSquare(intnum,double(*pf)(int)){return(*pf)(num*num);}
auto关键字用于自动类型推断:
autopc=pa;
自动类型推断只能用于单值初始化,而不能用于初始化列表。
除auto外,C++还提供了其他简化声明的工具。
typeofdoublereal;