本章重要点注意函数指针,const指针参数。
其他的其实都简简单单
第七章 函数——C++的编程模块(下)
回答下面的问题
问:为什么C++的函数不能直接返回一个字符串,而是返回字符串的地址?
问:在C++中,创建包含n个字符的字符串,为什么需要能够存储n + 1个字符的空间?
问:为什么不能使用
auto
初始化函数指针数组?
设计处理文本字符串的函数;
【总结知识点】
char * buildstr(char c, int n)
{
char * pstr = new char[n + 1];
pstr[n] = '\0'; // terminate string
while (n-- > 0)
pstr[n] = c; // fill rest of string
return pstr;
}
-
C++
的函数无法直接返回一个字符串,但可以返回字符串的地址(即返回指针)。用这种方式处理字符串的效率更高。 -
在
C++
中,如果要创建包含n个字符的字符串,需要能够存储n + 1
个字符的空间以便能够存储空值字符。因此,如果你请求分配n + 1
个字节的内存来存储该字符串,并将最后一个字节设置为空值字符。 -
在
C++
中,你可以使用new
和delete
来动态申请和释放内存。当你不再需要某个字符串时,可以使用delete
来释放其占用的内存。但是,当函数返回一个由new
分配的内存的指针时,程序员必须记住使用delete
来释放内存。
【重要问题及答案】
问题1:为什么C++的函数不能直接返回一个字符串,而是返回字符串的地址?
答:在C++中,所有的局部变量都会在函数调用完成后被销毁,所以如果函数试图直接返回一个局部字符串,那么在函数结束后,这个字符串就不存在了。但是,如果函数返回一个字符串的地址(即返回一个指针),那么这个地址所指向的字符串就可以在函数外部继续存在。
问题2:在C++中,创建包含n个字符的字符串,为什么需要能够存储n + 1个字符的空间?
答:在C++中,字符串是以’\0’作为结束标志的。所以如果一个字符串有n个字符,那么实际上需要有n+1个空间来存储这个字符串,其中额外的1个空间用来存储结束标志’\0’。
问题3:使用new和delete有哪些注意事项?
答:使用new和delete的时候要特别小心,因为如果忘记了释放内存,那就会造成内存泄漏。当你使用new分配了内存之后,一定要记得在适当的时候使用delete来释放内存。
设计处理结构的函数;
【总结知识点】
-
在
C++
中,当结构比较小时,最合理的方式是按值传递结构。 -
在
C++
中,当结构比较大时,最合理的方式是按地址传递结构。 -
参是指针时,应使用间接成员运算符(->),而不是成员运算符(句点)
-
C++
中的结构可以用于声明变量、函数的返回类型和函数的参数类型。
【重要问题及答案】
问题1:为什么在C++中,当结构比较小的时候,通常按值传递结构?
答:当结构体大小比较小的时候,按值传递结构体可以复制结构体时带来的额外开销不大。同时,按值传递可以保证原始数据的安全,因为函数不能修改实际参数。
问题2:C++中为什么需要使用结构?
答:在C++中,结构体是一种用户定义的数据类型,允许不同的数据元素被存储在同一个名字下。这使得程序员能够更有效地处理一组数据。
设计处理string对象的函数;
【总结知识点】
-
在
C++
中,string
对象与结构更相似。例如,可以将一个结构赋给另一个结构,也可以将一个对象赋给另一个对象。 -
可以将结构作为完整的实体传递给函数,也可以将对象作为完整的实体进行传递。
-
如果需要多个字符串,可以声明一个
string
对象数组,而不是二维char
数组。 -
在
C++
中,数组的每个元素都可以是一个string对象,可以通过常规的数组操作方式对其进行操作。
【重要问题及答案】
问题1:在C++
中如何处理string
对象数组?
答:在C++
中,你可以声明一个string
对象数组来存储多个字符串。例如,声明一个大小为5的string
数组:
string list[SIZE]; // an array holding 5 string object
然后,你可以使用常规的数组操作方法(如下标)来访问和修改数组中的string
对象。
问题2:如何将string
对象数组作为函数参数?
答:在C++
中,你可以将string
对象数组作为参数传递给函数。例如,下面的函数接受一个string
数组和一个整数n,然后打印出数组中的每个字符串:
void display(const string sa[], int n)
{
for (int i = 0; i < n; i++)
cout << i + 1 << ": " << sa[i] << endl;
}
在这个函数中,sa
就是一个指向string
对象的指针,你可以通过下标来访问数组中的每个元素。
问题3:如何使用string
对象进行输入操作?
答:在C++
中,你可以使用getline()
函数从输入流(例如cin
)读取一行文本,并将其存储在string
对象中。例如:
getline(cin,list[i]);
调用自身的函数(递归);
递归嘛和其他语言一样,随便讲讲。
知识点总结:
-
递归是一种函数调用自身的编程技术,通常在问题可以分解为相似子问题的情况下使用。
-
递归函数通常包含两部分:一个基本情况(终止条件)和一个递归情况。基本情况用于终止递归,而递归情况调用函数本身以解决更小规模的子问题。
-
递归调用会创建多个函数栈帧,每个栈帧都有自己的局部变量,包括函数参数。每个栈帧都会等待其递归调用返回后才能继续执行。
-
递归的深度取决于递归调用的次数,如果没有终止条件或者终止条件永远不会满足,递归会无限循环,然后导致栈溢出。
重要问题和答案:
-
什么是递归函数的基本结构?
递归函数通常包含两部分:一个基本情况(终止条件)和一个递归情况。基本情况用于终止递归,递归情况调用函数本身。
-
为什么需要终止条件?
终止条件是递归的退出条件,如果没有终止条件或者终止条件不满足,递归将无限循环,导致栈溢出或无法结束。
-
在递归调用中,每个函数栈帧都有什么内容?
每个函数栈帧包含了函数的局部变量、参数和返回地址。每个栈帧等待其递归调用返回后才能继续执行。
-
递归的深度是如何确定的?
递归的深度取决于递归函数被调用的次数。每次递归调用都会创建一个新的函数栈帧,导致深度增加。
-
为什么递归函数的局部变量可以有不同的值?
递归函数的局部变量在不同的函数栈帧中具有不同的值,因为每个函数栈帧都有自己的副本。这使得递归函数能够处理不同的数据。
-
递归函数在何时终止并返回?
递归函数在满足终止条件时终止,并返回到前一个递归调用的位置。然后依次返回,直到回到最初的函数调用位置。
指向函数的指针(1)
知识点总结:
-
函数指针是指向函数的指针,允许在运行时动态选择调用哪个函数。
-
获取函数的地址很简单,只需使用函数名(不带参数)即可。
-
声明函数指针时需要指定函数的返回类型和参数列表,以便编译器了解函数指针的类型。
double pam(int); // prototype
则正确的指针类型声明如下:
double (*pf)(int);
-
函数指针的声明使用括号将
*pf
括起来,以确保正确的运算符优先级。 -
使用函数指针来调用函数时,可以像使用函数名一样使用
pf
或(*pf)
,两种方式都是正确的。
double (*pf)(int); // pf points to a function that returns double
double *pf(int); // pf() a function that returns a pointer-to-double
- 函数指针允许将不同的函数传递给另一个函数,从而实现动态的函数调用。
重要问题和答案:
-
什么是函数指针?
函数指针是指向函数的指针,可以用来在运行时选择调用哪个函数。它允许动态地选择不同的函数。
-
如何获取函数的地址?
获取函数的地址很简单,只需使用函数名即可,不带参数。
-
声明函数指针时需要注意什么?
声明函数指针时需要指定函数的返回类型和参数列表,以确保编译器了解函数指针的类型。
-
如何使用函数指针来调用函数?
可以使用函数指针像使用函数名一样调用函数,例如
pf()
或(*pf)
。两种方式都是正确的。为何pf和(pf)等价呢?一种学派认为,由于pf是函数指针,而pf是函数,因此应将(*pf)()用作函数调用。另一种学派认为,由于函数名是指向该函数的指针,指向函数的指针的行为应与函数名相似,因此应将pf()用作函数调用使用。C++进行了折中——这两种方式都是正确的,或者至少是允许的,虽然它们在逻辑上是互相冲突的。在认为这种折中粗糙之前,应该想到,容忍逻辑上无法自圆其说的观点正是人类思维活动的特点。
-
函数指针有什么实际用途?
函数指针可以用于实现回调函数、动态函数选择和函数表等应用,使代码更加灵活和可扩展。例如,可以将不同的估算算法传递给一个估算函数,从而根据需要选择不同的算法。
-
为何pf和(*pf)等价呢?
-
一种学派认为,由于pf是函数指针,而*pf是函数,因此应将(*pf)()用作函数调用。另一种学派认为,由于函数名是指向该函数的指针,指向函数的指针的行为应与函数名相似,因此应将pf()用作函数调用使用。C++进行了折中——这两种方式都是正确的,或者至少是允许的,虽然它们在逻辑上是互相冲突的。在认为这种折中粗糙之前,应该想到,容忍逻辑上无法自圆其说的观点正是人类思维活动的特点。
指向函数的指针(2)
知识点总结:
下面是一些函数的原型,它们的特征标和返回类型相同:
const double * f1(const double ar[], int n);
const double * f2(const double [], int);
const double * f3(const double *, int);
假设要声明一个指针,它可指向这三个函数之一。假定该指针名为p1,则只需将目标函数原型中的函数名替换为(*p1):
const double * (*p1)(const double *, int) = f1;
-
函数指针数组允许存储多个函数指针,并通过索引来选择调用哪个函数。
-
函数指针的声明需要包括函数的返回类型和参数列表,以指定函数指针的类型。
-
函数指针数组的声明方式类似于单个函数指针的声明,但需要在某个地方添加
[n]
表示数组中有多少个函数指针。
重点来了!!!
const double * (*pa[3])(const double *, int) = {f1,f2,f3};
为何将[3]放在这个地方呢?pa是一个包含三个元素的数组,而要声明这样的数组,首先需要使用pa[3]。该声明的其他部分指出了数组包含的元素是什么样的。运算符[]的优先级高于*,因此*pa[3]表明pa是一个包含三个指针的数组。上述声明的其他部分指出了每个指针指向的是什么:特征标为const double *, int,且返回类型为const double *的函数。因此,pa是一个包含三个指针的数组,其中每个指针都指向这样的函数,即将const double *和int作为参数,并返回一个const double *。
-
使用函数指针数组时,可以使用循环来依次调用不同的函数。
-
函数指针的使用方式类似于调用函数,可以使用
(*pf)(args)
或pf(args)
,其中pf
是函数指针,args
是函数的参数。 -
自动类型推断
auto
可以用于声明函数指针,但不能用于初始化函数指针数组,因为初始化列表需要显式的类型信息。
重要问题和答案:
-
什么是函数指针数组?
函数指针数组是一个数组,其中的每个元素都是一个函数指针,允许存储多个函数的地址,并通过索引来选择调用哪个函数。
-
如何声明函数指针数组?
声明函数指针数组需要包括函数的返回类型和参数列表,以指定函数指针的类型,并在声明中使用
[n]
表示数组中有多少个函数指针。 -
如何使用函数指针数组来调用不同的函数?
可以使用循环来依次调用函数指针数组中的不同函数,例如
pf[i](args)
,其中pf
是函数指针数组,i
是索引,args
是函数的参数。 -
什么是自动类型推断
auto
?自动类型推断是C++11引入的功能,它允许编译器根据初始化表达式的类型推断变量的类型,使代码更简洁和易读。可以使用
auto
声明变量,让编译器自动确定变量的类型。 -
为什么不能使用
auto
初始化函数指针数组?自动类型推断
auto
适用于单值初始化,而函数指针数组通常需要显式的类型信息来初始化,因此不能使用auto
初始化函数指针数组。
用typedef关键字进行简化声明
本节讲解了在C++中使用typedef关键字进行简化声明,以创建类型别名。以下是知识点总结和一些重要问题及其答案:
知识点总结:
-
typedef关键字:typedef关键字用于创建类型别名,允许您为现有类型定义一个新的名称。
-
创建类型别名:通过使用typedef,您可以为任何现有类型(如int、double、函数指针等)创建一个新的名称。
-
示例:在示例中,使用typedef创建了一个函数指针类型的别名p_fun,该别名指向返回const double指针的函数,接受const double指针和int参数。
-
使用别名简化代码:一旦创建了类型别名,您可以使用该别名来声明变量,数组,指针等,从而减少代码的输入量,降低出错概率,提高代码可读性。
重要问题及答案:
-
什么是typedef关键字的作用?
答:typedef关键字的作用是创建类型别名,允许您为现有类型定义一个新的名称,使代码更易读,减少输入量,降低错误概率。
typedef double real; // makes real another name for double
-
在C++中如何使用typedef来创建类型别名?
答:使用typedef关键字,后跟新的类型名称和现有类型。例如:
typedef double real; // 创建名为real的double类型别名
-
在给定的示例中,p_fun是什么类型的别名?
typedef const double *(*p_fun)(const double *, int); // p_fun now a type name
p_fun p1 = f1; // p1 points to the f1() function
答:在给定的示例中,p_fun是一个函数指针类型的别名,指向返回const double指针的函数,接受const double指针和int参数。
-
为什么使用typedef来创建类型别名?
答:使用typedef可以提高代码的可读性和可维护性,减少输入量,降低出错概率,使代码更易理解。特别是在处理复杂的类型声明或函数指针时,typedef可以使代码更清晰。
问答区
问:为什么C++的函数不能直接返回一个字符串,而是返回字符串的地址?
答:在C++中,所有的局部变量都会在函数调用完成后被销毁,所以如果函数试图直接返回一个局部字符串,那么在函数结束后,这个字符串就不存在了。但是,如果函数返回一个字符串的地址(即返回一个指针),那么这个地址所指向的字符串就可以在函数外部继续存在。
问:在C++中,创建包含n个字符的字符串,为什么需要能够存储n + 1个字符的空间?
答:在C++中,字符串是以’\0’作为结束标志的。所以如果一个字符串有n个字符,那么实际上需要有n+1个空间来存储这个字符串,其中额外的1个空间用来存储结束标志’\0’。
问:为什么不能使用 auto
初始化函数指针数组?
答: 自动类型推断 auto
适用于单值初始化,而函数指针数组通常需要显式的类型信息来初始化,因此不能使用 auto
初始化函数指针数组。
问: 为什么使用typedef来创建类型别名?
答:使用typedef可以提高代码的可读性和可维护性,减少输入量,降低出错概率,使代码更易理解。特别是在处理复杂的类型声明或函数指针时,typedef可以使代码更清晰。