目录
标准错误流 ( cerr ) 和标准日志流 ( clog )
2. 示例:使用匿名 Lambda 函数来返回两个数中的较大数
输入输出
C++ 中的输入和输出(I/O)主要是通过标准库中的输入输出流来实现的。最常用的是 iostream 库,它提供了用于输入和输出的基本流类,包括 cin 、 cout 、 cerr 和 clog 。
标准输出流 ( cout )
- cout 代表标准输出流,通常用于向屏幕输出数据。
- 使用操作符 << (插入操作符)向 cout 发送数据。
- 例如, std::cout << "Hello, world!" << std::endl;( endl 相当于换行),会在屏幕上打印 "Hello, world!" 并换行。
标准输入流 ( cin )
- cin 代表标准输入流,用于从键盘接收数据。
- 使用操作符 >> (提取操作符)从 cin 提取数据。
- 例如, int x; std::cin >> x; 会从用户那里读取一个整数并存储在变量 x 中。
标准错误流 ( cerr ) 和标准日志流 ( clog )
- cerr 用于输出错误消息。与 cout 不同, cerr 不是缓冲的,这意味着它会立即输出。
- clog 类似于 cerr ,但它是缓冲的。它通常用于记录错误和日志信息。
编程示例
#include <iostream>
using namespace std;
int main()
{
int a;
int b;
// 输出
cout << "请输入数据" << endl;
// 输入
cin >> a;
cin >> b;
cout << a << "+" << b << "=" << a + b << endl;
// 标准错误流
cerr << "程序错误退出" << endl;
return 0;
}
基本变量类型
C++ 基本数据类型整理成表格(与C语言类似。)。以下是一个表格,展示了不同的基本数据类型及其一般用途和大小范围:
宽字符的用法
#include <iostream>
#include <locale>
#include <wchar.h>
int main() {
// 设置本地化以支持宽字符
std::setlocale(LC_ALL, "");
// 使用 wchar_t 类型定义一个宽字符串
wchar_t wstr[] = L"你好,世界!";
// 在 C++ 中打印宽字符串
std::wcout << wstr << std::endl;
return 0;
}
climits
在 C++ 中, <climits> (或在 C 中是 <limits.h> )是一个标准头文件,提供了关于整型限制的信 息。这个头文件中定义了各种整型数据类型的属性,如最大值、最小值等。使用这些信息可以帮助你了解在特定编译器和平台上各种数据类型的大小和范围。
如何使用 <climits>
要使用 <climits> 中定义的常量,你首先需要包含这个头文件:
#include <climits>
然后,你可以使用它提供的各种常量,例如:
- INT_MAX : int 类型的最大值。
- INT_MIN : int 类型的最小值。
- UINT_MAX : unsigned int 类型的最大值。
- LONG_MAX : long int 类型的最大值。
- LONG_MIN : long int 类型的最小值。
- LLONG_MAX : long long int 类型的最大值。
- LLONG_MIN : long long int 类型的最小值。
编程示例
#include <iostream>
#include <climits>
int main() {
std::cout << "The range of int is from " << INT_MIN << " to " << INT_MAX << std::endl;
std::cout << "The maximum value of unsigned int is " << UINT_MAX << std::endl;
std::cout << "The range of long long is from " << LLONG_MIN << " to " << LLONG_MAX << std::endl;
return 0;
}
这个程序会输出 int 、 unsigned int 和 long long int 类型的最大值和最小值。
输出结果:
注意事项
- <climits> 提供的是编译时确定的常量,这意味着这些值在编译时就已经固定,根据编译器和平台 的不同而可能有所不同。
- 使用这些限制值可以帮助你编写更可移植和安全的代码,特别是在处理可能超出数据类型范围的操作时。
流程控制
在 C++ 中,流程控制语句用于根据不同条件控制程序的执行流程。它们是编程中的基本构建块,允许程序根据条件执行不同的代码段,重复执行某些操作,或者根据特定情况跳过某些代码段。下面是 C++ 中最常见的流程控制语句:
条件语句
1. if 语句:基于条件的基本控制结构。如果条件为真,则执行代码块。
if (condition) {
// 条件为真时执行的代码
}
else 语句:与 if 语句配合使用,当 if 的条件为假时执行。
if (condition) {
// 条件为真时执行的代码
} else {
// 条件为假时执行的代码
}
else if 语句:用于测试多个条件。
if (condition1) {
// 第一个条件为真时执行的代码
} else if (condition2) {
// 第二个条件为真时执行的代码
} else {
// 所有条件为假时执行的代码
}
switch 语句:基于变量的值选择执行不同代码块的方法。
switch (expression) {
case value1:
// expression 等于 value1 时执行的代码
break;
case value2:
// expression 等于 value2 时执行的代码
break;
default:
// 没有匹配的 case 时执行的代码
}
循环语句
for 循环:当知道循环应该执行的次数时使用。
for (initialization; condition; increment) {
// 循环体
}
while 循环:当条件为真时,重复执行代码块。
while (condition) {
// 循环体
}
do-while 循环:至少执行一次循环体,然后再检查条件。
do {
// 循环体
} while (condition);
跳转语句
1. break 语句:用于立即跳出最近的 switch 或循环( for 、 while 、 do-while )。
2. continue 语句:跳过循环的当前迭代,并继续下一次迭代。
3. goto 语句:直接跳转到程序中的另一个点。使用 goto 通常不推荐,因为它可以使代码难以阅读和维护。
流程控制语句是编程中非常重要的部分,允许开发者编写可以根据不同情况改变行为的灵活且强大的程序。在使用这些语句时,应该确保逻辑清晰,以便代码易于理解和维护。
函数
在 C++ 中,函数是一段执行特定任务的代码块,它可以带有参数,并且可能返回一个值。函数的使用使得代码更加模块化和可重用,有助于降低代码的复杂性,并提高可维护性。
函数的基本结构
C++ 函数的基本结构包括返回类型、函数名、参数列表和函数体:
返回类型 函数名(参数列表) {
// 函数体
// 返回语句(如果有返回值的话)
}
编程示例
#include <iostream>
using namespace std;
int addFunc(int a, int b)
{
return a + b;
}
int main()
{
int a;
int b;
cout << "请输入两个整数" << endl;
cin >> a >> b;
cout << "结果为:" << addFunc(a,b) << endl;
return 0;
}
在这个示例中, addFunc 函数接收两个整数参数,并返回它们的和。
函数的组成部分
1. 返回类型:指定函数返回的数据类型。如果函数不返回任何值,则使用 void 。
2. 函数名:函数的标识符,用于调用函数。
3. 参数列表:括号内的变量列表,用于从函数的调用者那里接收值。如果函数不接收任何参数,则此列表为空。
4. 函数体:大括号 {} 内的一系列语句,定义了函数的执行操作。
内联函数
内联函数(Inline Function)是C++中一种特殊的函数,其定义直接在每个调用点展开。这意味着编译器会尝试将函数调用替换为函数本身的代码,这样可以减少函数调用的开销,尤其是在小型函数中。
特点
1. 减少函数调用开销:内联函数通常用于优化小型、频繁调用的函数,因为它避免了函数调用的常规开销(如参数传递、栈操作等)。
2. 编译器决策:
- 由编译器决定是否真正内联(即使使用
inline
关键字,也只是建议,并非强制)。 - 即使函数被声明为内联,编译器也可能决定不进行内联,特别是对于复杂或递归函数。
3. 适用于小型函数:通常只有简单的、执行时间短的函数适合做内联。
4. 定义在每个使用点:内联函数的定义(而非仅仅是声明)必须对每个使用它的文件都可见,通常意味着将内联函数定义在头文件中。
使用方法
通过在函数声明前添加关键字 inline 来指示编译器该函数适合内联:
inline int max(int x, int y) {
return x > y ? x : y;
}
在这个示例中,函数 addFunc 被定义为内联函数。当它被调用时,编译器可能会将函数调用替换为函数体内的代码。
编程示例
#include <iostream>
using namespace std;
// 添加 inline 关键字,声明该函数为内联函数
inline int addFunc(int a, int b)
{
return a + b;
}
int main()
{
int a,b;
cout << "请输入两个整数:" << endl;
cin >> a >> b;
cout << "结果为:" << addFunc(a, b) << endl;
return 0;
}
注意事项
- 过度使用的风险:不应滥用内联函数,因为这可能会增加最终程序的大小(代码膨胀)。对于大型函数或递归函数,内联可能导致性能下降。
- 编译器的决定:最终是否将函数内联是由编译器决定的,即使函数被标记为 inline 。
- 适用场景:最适合内联的是小型函数和在性能要求高的代码中频繁调用的函数。
内联函数是一种用于优化程序性能的工具,但需要合理使用,以确保代码的可维护性和性能的平衡。
auto
关键字
1. 自动类型推导
代码示例
#include <iostream>
#include <vector>
int main() {
// 自动推导基本数据类型
auto num = 10; // 编译器推断 num 的类型为 int
auto pi = 3.14; // 编译器推断 pi 的类型为 double
std::vector<int> vec = {1, 2, 3, 4, 5};
// 自动推导迭代器类型
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
auto num = 10;
:由于初始化值10
是一个整数,编译器自动推断num
的类型为int
。auto pi = 3.14;
:初始化值3.14
是一个双精度浮点数,编译器推断pi
的类型为double
。auto it = vec.begin();
:vec.begin()
返回的是std::vector<int>::iterator
类型,使用auto
可以避免手动书写这个复杂的类型,让代码更简洁。
2. 用于函数返回类型推导
auto
可以用于函数返回类型的推导,编译器会根据函数体中的返回语句自动推断返回类型。
编程示例
#include <iostream>
// 函数返回类型推导
auto add(int a, int b) {
return a + b;
}
int main() {
auto result = add(3, 5);
std::cout << "3 + 5 = " << result << std::endl;
return 0;
}
auto add(int a, int b)
:函数add
的返回类型使用auto
进行推导,编译器根据return a + b;
语句推断返回类型为int
。
3. 用于 lambda 表达式的返回类型推导
在 lambda 表达式中,auto
可以让编译器自动推断返回类型,通常情况下可以省略返回类型的显式指定。
编程示例
#include <iostream>
int main() {
// lambda 表达式返回类型推导
auto multiply = [](int a, int b) {
return a * b;
};
auto product = multiply(4, 6);
std::cout << "4 * 6 = " << product << std::endl;
return 0;
}
auto multiply = [](int a, int b) { return a * b; };
:lambda 表达式的返回类型由编译器根据return a * b;
语句自动推断为int
。
注意事项
- 必须初始化:使用
auto
声明变量时,必须进行初始化,因为编译器需要根据初始化表达式来推断类型。 - 类型可能会被截断:在某些情况下,
auto
推导的类型可能不是你期望的类型,例如在推导指针和引用时,需要特别注意。 - 模板和泛型编程:
auto
在模板和泛型编程中也非常有用,可以让代码更加通用和灵活。
Lambda 表达式
在C++中,匿名函数(也称为Lambda表达式)是一种可以在代码中内联定义的函数,而不需要显式地命名。
基本语法
[capture clause](parameters) -> return_type {
// 函数体
// 可以使用捕获列表中的变量
return expression; // 可选的返回语句
}
- 捕获列表(Capture clause):捕获列表,用于指定Lambda表达式可以访问的外部变量。捕获方式可以是值捕获、引用捕获或混合捕获。
- 参数列表(Parameters):参数列表,与普通函数的参数列表类似。
- 返回类型(Return type):Lambda 表达式可以自动推断返回类型,也可以显式指定返回类型 -> return_type 。如果函数体只有一条返回语句,可以省略返回类型。
- 函数体(Body):Lambda 表达式的函数体,包含需要执行的代码。
捕获列表
捕获列表用于指定Lambda表达式如何访问外部变量。常见的捕获方式有:
值捕获:
[=]
或[var1, var2]
[=]
:捕获所有外部变量,按值传递。[var1, var2]
:只捕获var1
和var2
,按值传递。
引用捕获:
[&]
或[&var1, &var2]
[&]
:捕获所有外部变量,按引用传递。[&var1, &var2]
:只捕获var1
和var2
,按引用传递。
混合捕获:
[=, &var1]
或[&, var1]
[=, &var1]
:按值捕获所有外部变量,但var1
按引用捕获。[&, var1]
:按引用捕获所有外部变量,但var1
按值捕获。
编程示例
Lambda 表达式最简单的案例是在需要一个小型函数或临时函数时直接使用它。以下是一个非常简单的例子,其中使用 Lambda 表达式来定义一个加法操作,并立即使用它来计算两个数的和。
1. 示例:使用 Lambda 表达式进行加法
#include <iostream>
using namespace std;
int main()
{
int a = 5;
int b = 6;
int ret;
auto add = [](int a,int b)->int{
return a + b;
};
ret = add(a,b);
cout << ret << endl;
return 0;
}
在这个例子中:
- 我们定义了一个名为 add 的 Lambda 表达式,它接受两个整数参数,并返回它们的和。
- 然后,我们使用这个 Lambda 表达式来计算两个数字(10 和 20)的和,并将结果存储在变量 ret 中。
- 最后,我们打印出这个和。
这个例子展示了 Lambda 表达式的基本用法:作为一种简洁而快速的方式来定义小型函数。
我们可以写一个例子,其中使用一个函数来找出两个数中的较大数,这个函数将接受一个 lambda 函数作为回调来比较这两个数。Lambda 函数将直接在函数调用时定义,完全是匿名的。
2. 示例:使用匿名 Lambda 函数来返回两个数中的较大数
#include <iostream>
int getMax(int a, int b, bool(*compare )(int, int))
{
if(compare (a,b))
{
return a;
}
else
{
return b;
}
}
int main()
{
int x = 10;
int y = 20;
int ret = getMax(x,y,[](int a,int b)->bool{
return a > b;
});
std:: cout << ret << std:: endl;
return 0;
}
- getMax 函数接受两个整数 a 和 b ,以及一个比较函数 compare 。这个比较函数是一个指向函数的指针(函数指针),它接受两个整数并返回一个布尔值。
- 在 main 函数中,我们调用 getMax ,并直接在调用点定义了一个匿名的 lambda 函数。这个 lambda 函数接受两个整数并返回一个表示第一个整数是否大于第二个整数的布尔值。
- 这个 lambda 函数在 getMax 中被用作比较两个数的逻辑。根据 lambda 函数的返回值, getMax 返回较大的数。
这个例子展示了如何直接在函数调用中使用匿名 lambda 函数,使代码更加简洁和直接。这种方法在需要临时函数逻辑的场合非常有用,尤其是在比较、条件检查或小型回调中。
在 Lambda 表达式中,参数捕获是指 Lambda 表达式从其定义的上下文中捕获变量的能力。这使得 Lambda 可以使用并操作在其外部定义的变量。捕获可以按值(拷贝)或按引用进行。
让我们通过一个简单的示例来展示带参数捕获的 Lambda 表达式。
3. 示例:使用带参数捕获的 Lambda 表达式
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int b = 20;
int c = 30;
int ret;
// 捕获外部变量a,b 这两个变量的值不可被更改,可以理解为拷贝一份到匿名函数的捕获列表
auto add = [a, b]()->int{
return a + b;
};
ret = add();
cout << ret << endl;
// 捕获外部所有变量(值捕获),同样这些变量的值不可被更改,可以理解为拷贝一份到匿名函数的捕获列表
auto multiplication = [=]()->int{
return a * b * c;
};
ret = multiplication();
cout << ret << endl;
// 捕获所有外部变量按引用捕获
auto allVariables = [&]()->int {
a++; // 修改变量 a 的实际值,引用捕获可以修改
return a + b + c;
};
ret = allVariables();
cout << ret << endl;
cout << a << endl;
return 0;
}
在这个例子中:
- 第一个 Lambda 表达式 add 按值捕获了 a 和 b (即它们的副本)。这意味着 sum 内的 x 和 y 是在 Lambda 定义时的值的拷贝。
- 第二个 Lambda 表达式 multiplication 使用 [=] 捕获列表,这表示它按值捕获所有外部变量。
- 第三个 Lambda 表达式 allVariables 使用 [&] 捕获列表,这表示它按引用捕获所有外部变量。
因此,它可以修改 x 和 y 的原始值。 这个示例展示了如何使用不同类型的捕获列表(按值和按引用)来控制 Lambda 表达式对外部变量的访问和修改。按值捕获是安全的,但不允许修改原始变量,而按引用捕获允许修改原始变量,但需要注意引用的有效性和生命周期问题。
以下是一个表格,概述了 Lambda 函数和内联函数在 C++ 中的相似之处和区别:
请注意,虽然 Lambda 函数和内联函数在某些方面有相似之处,如它们都可以被编译器优化以减少调用开销,但它们在设计和用途上有明显的不同。Lambda 函数的核心优势在于它们的匿名性和对外部变量的捕获能力,而内联函数则主要关注于提高小型函数的性能。
数组
在 C++ 中,数组是一种存储固定大小的相同类型元素的序列。数组的所有元素都存储在连续的内存位置上。这种数据结构非常适合于存储具有固定数量和相同数据类型的元素集合。
声明数组
声明数组的基本语法如下:
数据类型 数组名[数组大小];
例如,声明一个类型为 int 的数组,包含 10 个元素:
int myArray[10];
初始化数组
在声明数组时,您可以同时初始化数组:
int myArray[5] = {10, 20, 30, 40, 50};
如果您在初始化数组时没有指定所有元素的值,未初始化的元素将被自动设置为该数据类型的默认值(对于基本数据类型通常是 0):
int myArray[5] = {10, 20}; // 其余元素将被初始化为 0
访问数组元素
您可以通过指定索引来访问数组中的元素。数组索引是从 0 开始的,所以数组的第一个元素是 数组名[0] ,第二个元素是 数组名[1] ,依此类推:
int value = myArray[2]; // 访问第三个元素
示例
以下是使用数组的简单示例:
#include <iostream>
using namespace std;
int main() {
int myArray[5] = {10, 20, 30, 40, 50};
// 输出所有数组元素的值
for(int i = 0; i < 5; ++i) {
cout << "Element at index " << i << ": " << myArray[i] << endl;
}
return 0;
}
注意事项
- 数组的大小必须在编译时已知,且不能更改。
- 数组索引越界是常见的错误,可能会导致未定义的行为。
- 对于更复杂的用例,您可能需要使用 C++ 的标准模板库(STL)中的容器,如 std::vector ,它提供了可以动态改变大小的数组。
- 数组的元素存储在连续的内存位置上,这使得访问数组元素非常快。
练习
#include <iostream>
using namespace std;
int main()
{
int idata1 = 0;
int idata2 = 0;
char compute;
// Lambad 表达式,值捕获
auto add = [](int a, int b)->int{
return a + b;
};
auto subtraction = [](int a, int b)->int{
return a - b;
};
auto multiplication = [](int a, int b)->int{
return a * b;
};
auto division = [](int a, int b)->double{
return (double)a / b;
};
while(1)
{
cout << "请输入需计算的两个整数:" << endl;
cin >> idata1;
cin >> idata2;
cout << "请输入计算类型:+、-、*、/" << endl;
cin >> compute;
switch( compute )
{
case '+':
cout << add(idata1, idata2) << endl;
break;
case '-':
cout << subtraction(idata1, idata2) << endl;
break;
case '*':
cout << multiplication(idata1, idata2) << endl;
break;
case '/':
cout << division(idata1, idata2) << endl;
break;
}
}
return 0;
}
注意:例如,multiplication 如果使用引用捕获,那么 multiplication 这个 Lambda 表达式,需要写在 cin >> idata2 的后面。因为按上面代码的写的,引用捕获会引用 idata1,idata2 初始化赋值的数据,而不是引用 cin 之后的数据。
指针
C++完全兼容C语言指针,多出一个this指针,在面向对象中再学。
#include <iostream>
using namespace std;
void swap(int *pa, int *pb)
{
int tmp;
tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main() {
int a = 10;
int b = 20;
cout << a << endl;
cout << b << endl;
cout << "after chage:" << endl;
swap(&a,&b);
cout << a << endl;
cout << b << endl;
}