C++ 重难点学习手册

此文章是学习《C++ Primer第5版》的笔记与心得。目录C++ 重难点学习手册 姓名:王钟逸 日期:2021/07/11 - 2021/08/23 C++ 重难点学习手册第Ⅰ部分 C++基础1. cin >> a 作为 while 与 if 语句 condition 的分析2. 引用const 的引用(常量引用)3. 切忌混用 int 与 unsigned4. 范围 for (range for) 语句5. vector 对象动态增长的限制6. const 限.
摘要由CSDN通过智能技术生成

此文章是学习《C++ Primer第5版》的笔记与心得。

C++ 重难点学习手册

  • 作者:B52

  • 日期:2021/07/11 - 2021/08/23

目录

C++ 重难点学习手册

第Ⅰ部分 C++基础

1. cin >> a 作为 while 与 if 语句 condition 的分析

2. 引用

3. 切忌混用 int 与 unsigned

4. 范围 for (range for) 语句

5. vector 对象动态增长的限制

6. const 限定符

7. 左值与右值

8. decltype 与表达式

9. 求值顺序

10. 常用运算符

11. 函数形参尽量使用常量引用

12. 数组形参

13. 引用型函数

14. initializer_list 类型形参与列表初始化返回值

15. 返回数组指针与函数指针

16. 数据抽象和封装——类

第Ⅱ部分 C++标准库

第Ⅲ部分 类设计者的工具

面向对象程序设计

第Ⅳ部分 高级主题


第Ⅰ部分  C++ 基础

1. cin >> a 作为 while 与 if 语句 condition 的分析

首先请阅读以下这段程序,它的功能是统计每个值连续出现了多少次:

#include <iostream>
using namespace std;
int main()
{
    // currVal 是我们正在统计的数;我们将读入的新值存入 val
    int currVal = 0, val = 0;
    //读取第一个数, 并确保确实有数据可以处理
    if (cin >> currVal) {
        int cnt = 1;              // 保存我们正在处理的当前值的个数
        while(cin >> val) { // 读取剩余的数
            if (val == currVal)       // 如果值相同
                ++cnt;            // 将 cnt 加 1
            else{                     // 否则,打印前一个值的个数
                cout << currVal << " occurs "
                          << cnt << " times " << endl;
                currVal = val;    // 记住新数
                cnt = 1;              // 重置计数器
            }
        }// whi1e 循环在这里结束
        // 记住打印文件中最后一个值的个数
        cout << currVal << " occurs "
            << cnt << " times " << endl;
    } // 最外层的 if 语句在这里结束
    return 0;
}

《C++ Primer》书中写道:

对于以上程序,

按照书中操作,实际输出却是:

实际上,在输入全部数据后,按下回车只能输出前三条结果,之后无论按多少个回车都不会结束程序并返回第四条结果,针对这个问题,可行的解决方案有两条:1. 输入一个非 int 型数据;2. 输入文件结束符 ^Z (Windows 中为 Ctrl+z,UNIX 中为 Ctrl+d)。

  • 输入非 int 型数据,while(cin >> val) 判定为输入错误,条件的布尔值为 0,退出 while 循环,打印出最后一个值,结束程序。

  • 输入文件结束符 ^Z 原理与上一条类似,如下:

    在有的编译器上,以上输入并不能得到正确的输出,必须要通过回车将缓冲区中内容刷到设备中,然后输入 ^Z,才会识别认定为文件终止。

总结:在 while(cin >> a) 语句中,输入的回车 (换行符)只是将输入流中的内容刷到设备中,而不会被识别终止符,需要引入输入错误或者文件结束符。

2. 引用

基本定义引用不是对象,而是一个已经存在的对象的别名

主要特征

  • 引用必须被初始化,且会绑定其初始值对象 (绑定的是对象而非初始值),一旦被定义即绑定,不能更改对象。

  • 被引用类型的初始值一定是与引用类型相同的对象,且一定是左值

    int val = 1.01; // 合法,转换后val被赋值为1
    int& r1 = 1.01; // 不合法,引用的初始值必须是左值
    int& r2 = val;  // 合法
    int& r3;        // 不合法,引用必须被初始化

  • 任何情况 (除了 decltype) 下使用引用,等同于使用其绑定的对象,即引用是别名。

以上的主要特征针对的是通常情况下的引用,此外还有一种特例——const 的引用不满足其中的几条特征。

const 的引用(常量引用)

  • 允许为常量引用绑定非常量的对象、字面值甚至是一个表达式

    int i = 42;
    const int &r1 = i;      // 合法,允许常量引用绑定到一个int型对象
    const int &r2 = 42;     // 合法,初始值为一个int型常量
    const int &r3 = r1 * 2; // 合法
    int &r4 = r1 * 2;       // 不合法,引用的初始值必须是左值

    常量引用这一特性的具体原理见以下这个案例:

    double dval = 3.1415926;
    const int &r1 = dval;

    为了确保 r1 绑定一个整数,编译器将上述代码变成了如下形式:

    const int tmp = dval;
    const int &r1 = tmp;

    故实际上首先生成了一个整型常量 tmp,并进行了类型转换,然后将该整型常量的值作为 r1 的初始值。

    基于以上原理,我们可以做一组实验来探究常量引用的部分性质:

    测试代码如下:

    #include <iostream>
    using namespace std;
    int main()
    {
        int val = 42;
        const int& r1 = val;
        const int& r2 = val * 2;
        const int& r3 = val + 2;
        cout << r1 << " " << r2 << " " << r3 << endl;
        val = 0;
        cout << r1 << " " << r2 << " " << r3 << endl;
    ​
        double dval = 3.1415926;
        const int& r4 = dval;
        const int& r5 = dval * 2;
        const int& r6 = dval + 2;
        cout << r4 << " " << r5 << " " << r6 << endl;
        dval = 0;
        cout << r4 << " " << r5 << " " << r6 << endl;
    ​
        return 0;
    }

    运行结果如下:

    通过修改对象 valdval 的值,我们可以看到,只有 r1 真正绑定的是初始值对象,其他的常量引用均经过了一个中间整型常量 tmp 的转换。

    此外,我们还应注意到,常量引用 r1 绑定了一个int型对象 val,仅仅是指不能通过 r1 来改变 val 的值,但可以通过其他途径 (比如 val++) 来改变。

3. 切忌混用 int 与 unsigned

特别需要注意的 unsigned 有:string::size_type

#include <iostream>
using namespace std;
​
int main()
{
    int a = -1;
    unsigned b = 10000;
​
    if (a < b) cout << "B is bigger." << endl;
    else cout << "B is not bigger than a." << endl;
​
    return 0;
}

4. 范围 for (range for) 语句

用于遍历给定序列的每个元素并对序列的每个值进行某种操作。

// 不改变原字符串
string str("The string is abc123def");
for (auto c : str){
    if(isdigit(c))
        ++c;
    cout << c;
}
cout << endl;
cout << str << endl;

// 改变原字符串
string s = "Keep out!";
for (auto& c : s)
	c = toupper(c);

5. vector 对象动态增长的限制

vector 是 C++ 的一种特有容器,能够实现对象容量的高效增长。但其对象动态增长也有以下两点限制:

  • 不能在 range for 循环中向 vector 对象添加元素;

  • 任何可能改变 vector 对象容量的操作,比如 push_back,都会使 vector 的迭代器失效。

对于第一点,range for 的原理是在普通的 for 语句中用迭代器进行遍历,因而该限制可以归为第二点。

对于第二点,则需要了解 string

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值