Cpp札记

本文主要记录一些使用过程中对语法的理解。

lambda表达式:#

Lambda 表达式是 C++11 引入的一种新特性,它使得编写内联匿名函数更加简洁和灵活。Lambda 表达式不仅能捕获局部变量,还能创建函数闭包。以下是 Lambda 表达式的语法和功能的详细解释。

Lambda 表达式的基本语法#

Lambda 表达式的基本形式如下:

1
2
3
[capture](parameters) -> return_type {
// function body
};
  • **capture**:捕获列表,用于捕获作用域中的变量。
  • **parameters**:参数列表,与普通函数的参数列表相同。
  • **return_type**:返回类型,可以省略,如果编译器能自动推断返回类型。
  • **function body**:函数体,包含 Lambda 表达式执行的代码。

示例#

基本 Lambda 表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};

// 使用 Lambda 表达式打印每个数字
std::for_each(numbers.begin(), numbers.end(), [](int n) {
std::cout << n << " ";
});
std::cout << std::endl;

return 0;
}

带捕获列表的 Lambda 表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
int factor = 2;

// 捕获局部变量 factor 并将其用于乘法
std::for_each(numbers.begin(), numbers.end(), [factor](int &n) {
n *= factor;
});

for (int n : numbers) {
std::cout << n << " ";
}
std::cout << std::endl;

return 0;
}

带返回类型的 Lambda 表达式

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main() {
auto add = [](int a, int b) -> int {
return a + b;
};

std::cout << "5 + 3 = " << add(5, 3) << std::endl;

return 0;
}

捕获方式#

捕获列表可以包含局部变量,通过以下几种方式捕获:

  • 按值捕获(默认):捕获外部变量的副本。
  • 按引用捕获:捕获外部变量的引用。
  • 隐式捕获:根据上下文自动捕获变量,可以是按值或按引用。

示例:按值捕获

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

int main() {
int x = 10;
auto lambda = [x]() {
std::cout << "Captured by value: " << x << std::endl;
};
x = 20;
lambda(); // 输出: Captured by value: 10

return 0;
}

示例:按引用捕获

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

int main() {
int x = 10;
auto lambda = [&x]() {
std::cout << "Captured by reference: " << x << std::endl;
};
x = 20;
lambda(); // 输出: Captured by reference: 20

return 0;
}

示例:隐式捕获

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

int main() {
int x = 10;
int y = 20;

// 隐式按值捕获
auto lambda_by_value = [=]() {
std::cout << "x: " << x << ", y: " << y << std::endl;
};

// 隐式按引用捕获
auto lambda_by_reference = [&]() {
std::cout << "x: " << x << ", y: " << y << std::endl;
};

x = 30;
y = 40;
lambda_by_value(); // 输出: x: 10, y: 20
lambda_by_reference(); // 输出: x: 30, y: 40

return 0;
}

函数闭包#

Lambda 表达式可以捕获外部变量,并在 Lambda 表达式生命周期内保持其状态,这就形成了函数闭包。函数闭包在一些高级编程场景中非常有用,如回调函数、事件处理等。

示例:创建函数闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <functional>

std::function<int(int)> create_multiplier(int factor) {
return [factor](int value) {
return value * factor;
};
}

int main() {
auto multiplier = create_multiplier(5);
std::cout << "5 * 3 = " << multiplier(3) << std::endl; // 输出: 5 * 3 = 15

return 0;
}

捕获所有外部变量(C++14 引入)#

在 C++14 中,可以使用 [=][&] 捕获所有外部变量,或者使用 [*this] 捕获当前对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
int factor = 2;

// 捕获所有外部变量(按值捕获)
auto multiply = [=](int n) {
return n * factor;
};

for (int n : numbers) {
std::cout << multiply(n) << " ";
}
std::cout << std::endl;

return 0;
}

cin:#

在C++中,cin是标准输入流,用于从控制台读取输入数据。理解cin的使用规则以及其他输入方法的细节,对写出健壮的C++程序至关重要。下面将详细讲解cin的规则以及其他常用的输入方法。

1. cin的基本规则#

  • 按空白字符分割输入: cin会自动跳过空白字符(如空格、换行符、制表符),并将输入内容按空白字符分割。每次读取的内容是一个“词”(word),即一段不包含空白字符的连续字符序列。

    1
    2
    int a, b;
    cin >> a >> b;

    如果输入为 3 4,则 a 得到 3b 得到 4

  • 处理换行符: 当用户按下Enter键后,输入结束。cin会处理到换行符前的输入,换行符本身会被保留在输入缓冲区中。

  • 读取字符串: 当用 cin >> 读取字符串时,cin会读取直到第一个空白字符为止的内容。例如:

    1
    2
    string str;
    cin >> str;

    如果输入为 Hello World,则 str 得到 HelloWorld 会留在输入缓冲区。

2. cin的常见问题#

  • 处理剩余的输入: 如果读取整数、浮点数或字符后输入缓冲区中还有未处理的内容(如换行符),可能会影响后续的输入操作。可以使用 cin.ignore()cin.get() 来清空缓冲区。

    1
    2
    3
    int x;
    cin >> x;
    cin.ignore(); // 忽略缓冲区中剩余的字符,通常是换行符
  • 输入验证: cin不会自动验证输入的有效性。若输入的类型不匹配,cin会进入错误状态,无法继续读取输入,必须清除错误状态并清空输入缓冲区。

    1
    2
    3
    4
    5
    6
    int num;
    cin >> num;
    if (cin.fail()) { // 检查是否发生错误
    cin.clear(); // 清除错误状态
    cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 丢弃当前行
    }

3. 其他输入方法#

  • cin.get() 读取单个字符,包括空白字符。常用于读取字符或者处理输入缓冲区中的换行符。

    1
    2
    char ch;
    cin.get(ch);
    • 读取一行字符: 可以使用 cin.getline() 读取一整行输入,包括空格。
    1
    2
    char line[100];
    cin.getline(line, 100); // 从输入读取最多100个字符,遇到换行符结束
  • getline(cin, string) 读取一行输入并存入字符串对象string中,读取直到换行符。

    1
    2
    string line;
    getline(cin, line);

    cin >>相比,getline不会忽略空格,适用于读取整行的输入。

  • scanf C语言的输入函数,提供了更灵活的格式化输入控制。常用于精确控制输入格式。

    1
    2
    int a, b;
    scanf("%d %d", &a, &b);

    使用scanf时需要特别注意匹配输入的格式符。

  • fgets C语言函数,读取一行输入并存储在字符数组中,常用于读取较长或者包含空格的输入。

    1
    2
    char buffer[100];
    fgets(buffer, 100, stdin); // 从标准输入读取最多100个字符

4. 注意事项#

  • 混用cingetline 在使用cin读取数据后再用getline读取字符串时,可能需要先调用cin.ignore()来清除输入缓冲区中残留的换行符。

    1
    2
    3
    4
    5
    int num;
    cin >> num;
    cin.ignore(); // 清除换行符
    string line;
    getline(cin, line); // 现在可以正确读取整行输入
  • 处理输入错误:当cin遇到类型不匹配时,会进入错误状态,必须调用cin.clear()清除错误状态并使用cin.ignore()清除缓冲区中的无效输入。

  • 使用循环读取输入: 在某些情况下,可以使用循环不断读取输入,直到遇到特定条件(如EOF或特定字符)为止。

    1
    2
    3
    4
    string input;
    while (getline(cin, input)) {
    // 处理每一行输入
    }

5. 总结#

  • cin主要用于基本的格式化输入,而getline适用于读取整行文本。使用cin.get()cin.ignore()可以处理输入缓冲区中的字符和换行符。
  • **scanffgets**是C语言中的输入方法,提供了更加灵活的输入控制,但在C++中通常优先使用cingetline
  • 混合使用输入方法时,需要注意缓冲区的管理,避免意外的行为。

通过理解这些输入方法及其细节,可以更好地处理用户输入,写出更加健壮和高效的C++程序。

stl实用入门:#

点这里