C++ PrimerPlus 复习 第七章 函数——C++的编程模块(下)

第一章 命令编译链接文件 make文件

第二章 进入c++

第三章 处理数据

第四章 复合类型 (上)

第四章 复合类型 (下)

第五章 循环和关系表达式

第六章 分支语句和逻辑运算符

第七章 函数——C++的编程模块(上)

第七章 函数——C++的编程模块(下)

本章重要点注意函数指针,const指针参数。
其他的其实都简简单单


回答下面的问题
问:为什么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;
}
  1. C++的函数无法直接返回一个字符串,但可以返回字符串的地址(即返回指针)。用这种方式处理字符串的效率更高。

  2. C++中,如果要创建包含n个字符的字符串,需要能够存储n + 1个字符的空间以便能够存储空值字符。因此,如果你请求分配n + 1个字节的内存来存储该字符串,并将最后一个字节设置为空值字符。

  3. C++中,你可以使用newdelete来动态申请和释放内存。当你不再需要某个字符串时,可以使用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来释放内存。

设计处理结构的函数;

【总结知识点】

  1. C++中,当结构比较小时,最合理的方式是按值传递结构。

  2. C++中,当结构比较大时,最合理的方式是按地址传递结构。

  3. 参是指针时,应使用间接成员运算符(->),而不是成员运算符(句点)

  4. C++中的结构可以用于声明变量、函数的返回类型和函数的参数类型。

【重要问题及答案】

问题1:为什么在C++中,当结构比较小的时候,通常按值传递结构?
答:当结构体大小比较小的时候,按值传递结构体可以复制结构体时带来的额外开销不大。同时,按值传递可以保证原始数据的安全,因为函数不能修改实际参数。

问题2:C++中为什么需要使用结构?
答:在C++中,结构体是一种用户定义的数据类型,允许不同的数据元素被存储在同一个名字下。这使得程序员能够更有效地处理一组数据。

设计处理string对象的函数;

【总结知识点】

  1. C++中,string对象与结构更相似。例如,可以将一个结构赋给另一个结构,也可以将一个对象赋给另一个对象。

  2. 可以将结构作为完整的实体传递给函数,也可以将对象作为完整的实体进行传递。

  3. 如果需要多个字符串,可以声明一个string对象数组,而不是二维char数组。

  4. 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. 递归是一种函数调用自身的编程技术,通常在问题可以分解为相似子问题的情况下使用。

  2. 递归函数通常包含两部分:一个基本情况(终止条件)和一个递归情况。基本情况用于终止递归,而递归情况调用函数本身以解决更小规模的子问题。

  3. 递归调用会创建多个函数栈帧,每个栈帧都有自己的局部变量,包括函数参数。每个栈帧都会等待其递归调用返回后才能继续执行。

  4. 递归的深度取决于递归调用的次数,如果没有终止条件或者终止条件永远不会满足,递归会无限循环,然后导致栈溢出。

重要问题和答案:

  1. 什么是递归函数的基本结构?

    递归函数通常包含两部分:一个基本情况(终止条件)和一个递归情况。基本情况用于终止递归,递归情况调用函数本身。

  2. 为什么需要终止条件?

    终止条件是递归的退出条件,如果没有终止条件或者终止条件不满足,递归将无限循环,导致栈溢出或无法结束。

  3. 在递归调用中,每个函数栈帧都有什么内容?

    每个函数栈帧包含了函数的局部变量、参数和返回地址。每个栈帧等待其递归调用返回后才能继续执行。

  4. 递归的深度是如何确定的?

    递归的深度取决于递归函数被调用的次数。每次递归调用都会创建一个新的函数栈帧,导致深度增加。

  5. 为什么递归函数的局部变量可以有不同的值?

    递归函数的局部变量在不同的函数栈帧中具有不同的值,因为每个函数栈帧都有自己的副本。这使得递归函数能够处理不同的数据。

  6. 递归函数在何时终止并返回?

    递归函数在满足终止条件时终止,并返回到前一个递归调用的位置。然后依次返回,直到回到最初的函数调用位置。

指向函数的指针(1)

知识点总结:

  1. 函数指针是指向函数的指针,允许在运行时动态选择调用哪个函数。

  2. 获取函数的地址很简单,只需使用函数名(不带参数)即可。

  3. 声明函数指针时需要指定函数的返回类型和参数列表,以便编译器了解函数指针的类型。

double pam(int); // prototype
则正确的指针类型声明如下:
double (*pf)(int);

  1. 函数指针的声明使用括号将*pf括起来,以确保正确的运算符优先级。

  2. 使用函数指针来调用函数时,可以像使用函数名一样使用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
  1. 函数指针允许将不同的函数传递给另一个函数,从而实现动态的函数调用。

重要问题和答案:

  1. 什么是函数指针?

    函数指针是指向函数的指针,可以用来在运行时选择调用哪个函数。它允许动态地选择不同的函数。

  2. 如何获取函数的地址?

    获取函数的地址很简单,只需使用函数名即可,不带参数。

  3. 声明函数指针时需要注意什么?

    声明函数指针时需要指定函数的返回类型和参数列表,以确保编译器了解函数指针的类型。

  4. 如何使用函数指针来调用函数?

    可以使用函数指针像使用函数名一样调用函数,例如 pf()(*pf)。两种方式都是正确的。

    为何pf和(pf)等价呢?一种学派认为,由于pf是函数指针,而pf是函数,因此应将(*pf)()用作函数调用。另一种学派认为,由于函数名是指向该函数的指针,指向函数的指针的行为应与函数名相似,因此应将pf()用作函数调用使用。C++进行了折中——这两种方式都是正确的,或者至少是允许的,虽然它们在逻辑上是互相冲突的。在认为这种折中粗糙之前,应该想到,容忍逻辑上无法自圆其说的观点正是人类思维活动的特点。

  5. 函数指针有什么实际用途?

    函数指针可以用于实现回调函数、动态函数选择和函数表等应用,使代码更加灵活和可扩展。例如,可以将不同的估算算法传递给一个估算函数,从而根据需要选择不同的算法。

  6. 为何pf和(*pf)等价呢?

  7. 一种学派认为,由于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;
  1. 函数指针数组允许存储多个函数指针,并通过索引来选择调用哪个函数。

  2. 函数指针的声明需要包括函数的返回类型和参数列表,以指定函数指针的类型。

  3. 函数指针数组的声明方式类似于单个函数指针的声明,但需要在某个地方添加 [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 *。

  1. 使用函数指针数组时,可以使用循环来依次调用不同的函数。

  2. 函数指针的使用方式类似于调用函数,可以使用 (*pf)(args)pf(args),其中 pf 是函数指针,args 是函数的参数。

  3. 自动类型推断 auto 可以用于声明函数指针,但不能用于初始化函数指针数组,因为初始化列表需要显式的类型信息。

重要问题和答案:

  1. 什么是函数指针数组?

    函数指针数组是一个数组,其中的每个元素都是一个函数指针,允许存储多个函数的地址,并通过索引来选择调用哪个函数。

  2. 如何声明函数指针数组?

    声明函数指针数组需要包括函数的返回类型和参数列表,以指定函数指针的类型,并在声明中使用 [n] 表示数组中有多少个函数指针。

  3. 如何使用函数指针数组来调用不同的函数?

    可以使用循环来依次调用函数指针数组中的不同函数,例如 pf[i](args),其中 pf 是函数指针数组,i 是索引,args 是函数的参数。

  4. 什么是自动类型推断 auto

    自动类型推断是C++11引入的功能,它允许编译器根据初始化表达式的类型推断变量的类型,使代码更简洁和易读。可以使用 auto 声明变量,让编译器自动确定变量的类型。

  5. 为什么不能使用 auto 初始化函数指针数组?

    自动类型推断 auto 适用于单值初始化,而函数指针数组通常需要显式的类型信息来初始化,因此不能使用 auto 初始化函数指针数组。

用typedef关键字进行简化声明

本节讲解了在C++中使用typedef关键字进行简化声明,以创建类型别名。以下是知识点总结和一些重要问题及其答案:

知识点总结:

  1. typedef关键字:typedef关键字用于创建类型别名,允许您为现有类型定义一个新的名称。

  2. 创建类型别名:通过使用typedef,您可以为任何现有类型(如int、double、函数指针等)创建一个新的名称。

  3. 示例:在示例中,使用typedef创建了一个函数指针类型的别名p_fun,该别名指向返回const double指针的函数,接受const double指针和int参数。

  4. 使用别名简化代码:一旦创建了类型别名,您可以使用该别名来声明变量,数组,指针等,从而减少代码的输入量,降低出错概率,提高代码可读性。

重要问题及答案:

  1. 什么是typedef关键字的作用?

    答:typedef关键字的作用是创建类型别名,允许您为现有类型定义一个新的名称,使代码更易读,减少输入量,降低错误概率。

typedef double real; // makes real another name for double
  1. 在C++中如何使用typedef来创建类型别名?

    答:使用typedef关键字,后跟新的类型名称和现有类型。例如:

    typedef double real; // 创建名为real的double类型别名
    
  2. 在给定的示例中,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参数。

  1. 为什么使用typedef来创建类型别名?

    答:使用typedef可以提高代码的可读性和可维护性,减少输入量,降低出错概率,使代码更易理解。特别是在处理复杂的类型声明或函数指针时,typedef可以使代码更清晰。

问答区

问:为什么C++的函数不能直接返回一个字符串,而是返回字符串的地址?

答:在C++中,所有的局部变量都会在函数调用完成后被销毁,所以如果函数试图直接返回一个局部字符串,那么在函数结束后,这个字符串就不存在了。但是,如果函数返回一个字符串的地址(即返回一个指针),那么这个地址所指向的字符串就可以在函数外部继续存在。

问:在C++中,创建包含n个字符的字符串,为什么需要能够存储n + 1个字符的空间?

答:在C++中,字符串是以’\0’作为结束标志的。所以如果一个字符串有n个字符,那么实际上需要有n+1个空间来存储这个字符串,其中额外的1个空间用来存储结束标志’\0’。

问:为什么不能使用 auto 初始化函数指针数组?

答: 自动类型推断 auto 适用于单值初始化,而函数指针数组通常需要显式的类型信息来初始化,因此不能使用 auto 初始化函数指针数组。

问: 为什么使用typedef来创建类型别名?

答:使用typedef可以提高代码的可读性和可维护性,减少输入量,降低出错概率,使代码更易理解。特别是在处理复杂的类型声明或函数指针时,typedef可以使代码更清晰。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值