【C++】——入门基础知识超详解

​​​​​​​

目录

​编辑

1.C++关键字

2. 命名空间

2.1 命名空间定义

2.2 命名空间使用

命名空间的使用有三种方式:

注意事项

3. C++输入&输出

示例 1:基本输入输出

示例 2:读取多个值

示例 3:处理字符串输入

示例 4:读取整行字符串

示例 5:错误输出和日志输出

输入输出流的格式化

示例 6:设置小数位数

示例 7:对齐输出

综合示例:简单的交互程序

4.缺省参数

4.1 缺省参数概念

4.2 缺省参数分类

注意事项

5. 函数重载

5.1 函数重载概念

5.2 C++支持函数重载的原理 -- 名字修饰 (Name Mangling)

结论

6. 引用

6.1 引用概念

6.2 引用特性

1.引用在定义时必须初始化

2.一个变量可以有多个引用

3.引用一旦引用一个实体,再不能引用其他实体

6.3 常引用

6.4 使用场景

1.做参数

2.做返回值

6.5 传值、传引用效率比较

6.6 引用和指针的区别

7. 内联函数

7.1 概念

7.2 特性

以空间换时间:

编译器建议:

3.声明和定义不分离:

7.3 内联函数的使用建议

8. auto 关键字

8.1 类型别名思考

8.2 auto 简介

8.3 auto 的使用细则

1.auto 与指针和引用结合使用

2.在同一行定义多个变量

8.4 auto 不能推导的场景

1.auto 不能作为函数的参数

2.auto 不能直接用来声明数组

3.为了避免与 C++98 中的 auto 混淆,C++11 只保留了 auto 作为类型指示符的用法

4.auto 最常见的优势用法是与 C++11 提供的新式 for 循环和 lambda 表达式配合使用

9. 基于范围的 for 循环

9.1 范围 for 的语法

9.2 范围 for 的使用条件

循环迭代的范围必须是确定的

迭代的对象要实现 ++ 和 == 的操作

10. 指针空值 nullptr

10.1 C++98 中的指针空值

10.2 nullptr 简介

注意:


1.C++关键字


C++总计63个关键字,C语言32个关键字
ps:下面我们只是看一下C++有多少关键字,不对关键字进行具体的讲解。后面我们学到以后再
细讲。

2. 命名空间


在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存
在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,
以避免命名冲突或名字污染
,namespace关键字的出现就是针对这种问题的。 

#include <stdio.h>
#include <stdlib.h>
int rand = 10;
// C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
int main()
{
printf("%d\n", rand);
return 0;
}
// 编译后后报错:error C2365: “rand”: 重定义;以前的定义是“函数”

2.1 命名空间定义


定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}
中即为命名空间的成员。

// bit是命名空间的名字,一般开发中是用项目名字做命名空间名。
// 大家下去以后自己练习用自己名字缩写即可,如张三:zs// 1. 正常的命名空间定义
namespace bit
{// 命名空间中可以定义变量/函数/类型int rand = 10;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};
}// 2. 命名空间可以嵌套
// test.cpp
namespace N1
{int a;int b;int Add(int left, int right){return left + right;}namespace N2{int c;int d;int Sub(int left, int right){return left - right;}} // namespace N2
} // namespace N1// 3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
// ps:一个工程中的test.h和上面test.cpp中两个N1会被合并成一个
// test.h
namespace N1
{int Mul(int left, int right){return left * right;}
} // namespace N1

注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中

2.2 命名空间使用


命名空间中成员该如何使用呢   比如:

#include <iostream> // 引入iostream以使用std::cout和std::endlnamespace bit
{// 命名空间中可以定义变量/函数/类型int a = 0;int b = 1;int Add(int left, int right){return left + right;}struct Node{struct Node* next;int val;};
}int main()
{// 使用命名空间中的变量需要加上命名空间前缀// 编译报错:error C2065: “a”: 未声明的标识符std::cout << bit::a << std::endl;return 0;
}

命名空间的使用有三种方式:


1.加命名空间名称及作用域限定符

这是最为明确的方式,通过加上命名空间名称和作用域限定符 :: 来访问命名空间中的成员。

#include <iostream>namespace bit {int a = 10;int b = 20;int Add(int left, int right) {return left + right;}
}int main() {// 使用命名空间名称及作用域限定符std::cout << "a: " << bit::a << std::endl;std::cout << "b: " << bit::b << std::endl;std::cout << "Add: " << bit::Add(3, 4) << std::endl;return 0;
}

2.使用using将命名空间中某个成员引入

使用 using 关键字可以将命名空间中的某个成员引入当前作用域,之后可以直接使用该成员。

#include <iostream>namespace bit {int a = 10;int b = 20;int Add(int left, int right) {return left + right;}
}int main() {// 使用using将命名空间中某个成员引入using bit::a;using bit::Add;std::cout << "a: " << a << std::endl;// 使用被引入的成员不需要加命名空间前缀std::cout << "Add: " << Add(3, 4) << std::endl;// 需要加命名空间前缀访问其他成员std::cout << "b: " << bit::b << std::endl;return 0;
}

3.使用using namespace 命名空间名称 引入

使用 using namespace 关键字可以将整个命名空间引入当前作用域,之后可以直接使用命名空间中的所有成员。

#include <iostream>namespace bit {int a = 10;int b = 20;int Add(int left, int right) {return left + right;}
}int main() {// 使用using将命名空间中某个成员引入using bit::a;using bit::Add;std::cout << "a: " << a << std::endl;// 使用被引入的成员不需要加命名空间前缀std::cout << "Add: " << Add(3, 4) << std::endl;// 需要加命名空间前缀访问其他成员std::cout << "b: " << bit::b << std::endl;return 0;
}
注意事项
  • 加命名空间名称及作用域限定符:这种方式最为安全和明确,避免了命名冲突。
  • 使用 using 将命名空间中某个成员引入:适用于只需要频繁使用命名空间中的某几个成员的情况。
  • 使用 using namespace 引入整个命名空间:简单快捷,但容易引发命名冲突,尤其是在大型项目中使用多个命名空间时。

根据实际需要选择合适的方式使用命名空间,有助于代码的组织和可读性。

3. C++输入&输出

在C++中,标准输入和输出通过标准库 <iostream> 提供。常用的输入输出流对象包括:

  • std::cin:标准输入流,用于从键盘读取输入。
  • std::cout:标准输出流,用于向屏幕输出信息。
  • std::cerr:标准错误流,用于向屏幕输出错误信息。
  • std::clog:标准日志流,用于向屏幕输出日志信息。

以下是一些常见的输入和输出操作的示例。

示例 1:基本输入输出
#include <iostream>int main() {int number;std::cout << "请输入一个整数: "; // 输出提示信息std::cin >> number;              // 从键盘读取输入到变量numberstd::cout << "您输入的整数是: " << number << std::endl; // 输出输入的值return 0;
}
  • std::cout 使用 << 操作符将字符串和变量输出到控制台。
  • std::cin 使用 >> 操作符从控制台读取输入到变量。
示例 2:读取多个值
#include <iostream>int main() {int a, b;std::cout << "请输入两个整数: ";std::cin >> a >> b;std::cout << "您输入的整数是: " << a << " 和 " << b << std::endl;return 0;
}
  • 可以使用多个 >> 操作符连续读取多个值。
示例 3:处理字符串输入
#include <iostream>
#include <string>int main() {std::string name;std::cout << "请输入您的姓名: ";std::cin >> name;std::cout << "您好, " << name << "!" << std::endl;return 0;
}
  • 对于单词输入,std::cin 可以直接读取到 std::string 变量中。
示例 4:读取整行字符串
#include <iostream>
#include <string>int main() {std::string line;std::cout << "请输入一行文字: ";std::getline(std::cin, line);std::cout << "您输入的是: " << line << std::endl;return 0;
}
  • std::getline 函数用于读取一整行输入,包括空格。
示例 5:错误输出和日志输出
#include <iostream>int main() {int a;std::cout << "请输入一个正整数: ";std::cin >> a;if (a <= 0) {std::cerr << "错误: 输入的不是正整数!" << std::endl;} else {std::clog << "输入的正整数是: " << a << std::endl;}return 0;
}
  • std::cerr 用于输出错误信息。
  • std::clog 用于输出日志信息。

输入输出流的格式化

C++ 提供了一些操作符和函数来格式化输入输出,例如控制小数位数、对齐等。

示例 6:设置小数位数
#include <iostream>
#include <iomanip> // 引入iomanip库int main() {double pi = 3.141592653589793;std::cout << "默认输出: " << pi << std::endl;std::cout << std::fixed << std::setprecision(2);std::cout << "设置两位小数: " << pi << std::endl;return 0;
}
  • std::fixedstd::setprecision 用于设置小数位数。
示例 7:对齐输出
#include <iostream>
#include <iomanip>int main() {std::cout << std::setw(10) << "列1" << std::setw(10) << "列2" << std::endl;std::cout << std::setw(10) << 123 << std::setw(10) << 456 << std::endl;std::cout << std::setw(10) << 78 << std::setw(10) << 91011 << std::endl;return 0;
}
  • std::setw 用于设置字段宽度,实现对齐输出。

综合示例:简单的交互程序

#include <iostream>
#include <string>int main() {std::string name;int age;double salary;std::cout << "请输入您的姓名: ";std::getline(std::cin, name);std::cout << "请输入您的年龄: ";std::cin >> age;std::cout << "请输入您的工资: ";std::cin >> salary;std::cout << std::fixed << std::setprecision(2);std::cout << "姓名: " << name << std::endl;std::cout << "年龄: " << age << " 岁" << std::endl;std::cout << "工资: $" << salary << std::endl;return 0;
}

4.缺省参数

4.1 缺省参数概念

缺省参数是在声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有传递相应的实参,则使用该参数的默认值,否则使用传递的实参。

例子:

#include <iostream>// 定义带缺省参数的函数
void PrintMessage(std::string message = "Hello, World!") {std::cout << message << std::endl;
}int main() {PrintMessage(); // 使用缺省参数,输出 "Hello, World!"PrintMessage("Hello, C++!"); // 使用指定的实参,输出 "Hello, C++!"return 0;
}

4.2 缺省参数分类

缺省参数可以分为全缺省参数和半缺省参数。

  1. 全缺省参数: 所有参数都有缺省值的函数。

    例子:

    #include <iostream>// 定义带全缺省参数的函数
    void Display(int a = 1, int b = 2) {std::cout << "a = " << a << ", b = " << b << std::endl;
    }int main() {Display(); // 使用缺省参数,输出 "a = 1, b = 2"Display(5); // 第一个参数使用指定值,第二个参数使用缺省值,输出 "a = 5, b = 2"Display(3, 4); // 使用指定的实参,输出 "a = 3, b = 4"return 0;
    }
    

    2.半缺省参数: 部分参数有缺省值的函数。通常缺省参数应从右往左定义,即后面的参数有缺省值,前面的没有。

    例子:

    #include <iostream>// 定义带半缺省参数的函数
    void Show(int a, int b = 10) {std::cout << "a = " << a << ", b = " << b << std::endl;
    }int main() {Show(5); // 第一个参数使用指定值,第二个参数使用缺省值,输出 "a = 5, b = 10"Show(3, 7); // 使用指定的实参,输出 "a = 3, b = 7"return 0;
    }
    

    注意事项

1.顺序:缺省参数必须从右向左依次定义,不能间隔定义。例如,void func(int a = 1, int b); 是不允许的,因为 b 没有缺省值,而 a 有。

// 错误示例:
void func(int a = 1, int b); // 不允许的,b 没有缺省值// 正确示例:
void func(int a, int b = 2); // 正确的,b 有缺省值

2.声明和定义:缺省参数只能在函数声明或定义中的一个地方出现,不能在两个地方都定义缺省值。

// 错误示例:
void func(int a = 1, int b = 2); // 在声明中定义了缺省参数void func(int a = 1, int b = 2) { // 在定义中又定义了缺省参数,这是错误的// function body
}// 正确示例:
void func(int a = 1, int b = 2); // 在声明中定义了缺省参数void func(int a, int b) { // 在定义中使用声明中的缺省参数// function body
}

3.缺省值必须是常量或全局变量:缺省值必须是一个常量或全局变量,不能是局部变量或表达式的结果。

const int default_value = 5;// 正确示例:
void func(int a, int b = default_value);// 错误示例:
void func(int a, int b = a + 1); // 不允许,缺省值不能是表达式的结果

4.C语言不支持缺省参数:缺省参数是C++的特性,C语言不支持缺省参数。

通过使用缺省参数,可以使函数调用更加简洁,避免在多次调用中重复传递相同的实参。

5. 函数重载

在自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词的真实含义,即该词被重载了。比如,关于体育项目的笑话:“乒乓球是‘谁也赢不了!’(我们赢不了对手),男足是‘谁也赢不了!’(我们赢不了对手)。”这展示了同一个表达可以有不同的解释。

同样地,在C++中,函数也可以重载。

5.1 函数重载概念

函数重载:是指在同一作用域中声明几个功能类似但参数不同的同名函数。这些同名函数的参数列表(参数个数、类型或类型顺序)不同。函数重载常用于处理实现功能类似但数据类型不同的问题。

例子:

#include <iostream>// 函数重载示例
void Print(int i) {std::cout << "整数: " << i << std::endl;
}void Print(double d) {std::cout << "双精度: " << d << std::endl;
}void Print(const std::string& s) {std::cout << "字符串: " << s << std::endl;
}int main() {Print(42);            // 调用 Print(int)Print(3.14);          // 调用 Print(double)Print("Hello!");      // 调用 Print(const std::string&)return 0;
}

5.2 C++支持函数重载的原理 -- 名字修饰 (Name Mangling)

为什么C++支持函数重载,而C语言不支持呢?这是因为C++使用了一种叫做名字修饰(Name Mangling)的技术。

在C/C++中,一个程序要运行,需要经历以下几个阶段:预处理、编译、汇编、链接。

  1. 实际项目通常由多个头文件和多个源文件构成。编译链接阶段,如果在 a.cpp 中调用了 b.cpp 中定义的 Add 函数,编译后链接前,a.o 的目标文件中没有 Add 的函数地址,因为 Add 定义在 b.cpp 中。所以链接阶段,链接器会到 b.o 的符号表中找到 Add 的地址,然后将它们链接到一起。

  2. 链接时,面对 Add 函数,链接器会使用函数名修饰规则来找到函数。不同编译器有不同的函数名修饰规则。

  3. 由于 Windows 下 VS 的修饰规则过于复杂,而 Linux 下 G++ 的修饰规则简单易懂,我们使用 G++ 演示修饰后的名字。

  4. 在 G++ 中,函数名修饰后的名字是 _Z + 函数长度 + 函数名 + 类型首字母

例子:

采用C语言编译器编译后:

// test.c
int Add(int a, int b) {return a + b;
}

编译后函数名未发生改变:

$ gcc -c test.c
$ nm test.o
0000000000000000 T Add

采用C++编译器编译后:

// test.cpp
int Add(int a, int b) {return a + b;
}

编译后函数名发生改变:

$ g++ -c test.cpp
$ nm test.o
0000000000000000 T _Z3Addii

通过名字修饰,C++ 可以区分同名函数,只要参数不同,修饰后的名字就不一样,支持函数重载。而C语言无法支持重载,因为同名函数无法区分。

结论

  • C语言不支持函数重载,因为同名函数无法区分。
  • C++支持函数重载,通过名字修饰技术将参数类型信息添加到函数名中,使得同名函数可以区分。
  • 两个函数如果函数名和参数都相同,即使返回值不同,也不构成重载,因为编译器无法区分它们。

6. 引用

6.1 引用概念

引用是C++中一个重要的概念,它并不是定义一个新变量,而是给已经存在的变量取了一个别名。引用和被引用的变量共享同一块内存空间,因此引用不会占用额外的内存空间。

语法:

类型& 引用变量名 = 实体变量;

例子:

int a = 10;
int& ref = a; // ref 是 a 的引用
  • 这里 refa 的别名,通过 ref 可以操作 a

注意: 引用类型必须和引用实体是同种类型。

6.2 引用特性

  • 1.引用在定义时必须初始化

    int a = 10;
    int& ref = a; // 必须初始化
    
  • 2.一个变量可以有多个引用

    int a = 10;
    int& ref1 = a;
    int& ref2 = a; // a 有多个引用
    
  • 3.引用一旦引用一个实体,再不能引用其他实体

    int a = 10;
    int b = 20;
    int& ref = a;
    // ref = b; // 错误,ref 不能再引用 b,只能修改 a 的值
    ref = b; // 这只是把 b 的值赋给 a,而不是让 ref 引用 b
    

    6.3 常引用

    常引用(const reference)指向一个不能修改的变量,这样可以防止通过引用修改变量的值。

    例子:

    int a = 10;
    const int& ref = a; // ref 是常引用,不能通过 ref 修改 a 的值
    // ref = 20; // 错误,不能修改
    

    6.4 使用场景

1.做参数

void printValue(const int& value) {std::cout << value << std::endl;
}int main() {int a = 10;printValue(a); // 传递引用return 0;
}

2.做返回值

int& getElement(int arr[], int index) {return arr[index];
}int main() {int arr[3] = {1, 2, 3};getElement(arr, 1) = 10; // 修改 arr[1] 的值std::cout << arr[1] << std::endl; // 输出 10return 0;
}

注意: 如果函数返回时,返回的对象还在作用域内(没有被销毁),则可以使用引用返回,否则必须使用传值返回。

6.5 传值、传引用效率比较

传值时,函数会传递实参的一份拷贝,这在处理大数据时效率低。传引用则直接操作实参,提高效率。

例子:

void printByValue(std::string s) {std::cout << s << std::endl;
}void printByReference(const std::string& s) {std::cout << s << std::endl;
}int main() {std::string str = "Hello, World!";printByValue(str); // 传值,效率低printByReference(str); // 传引用,效率高return 0;
}

6.6 引用和指针的区别

  1. 引用是变量的别名,指针存储变量的地址
    int a = 10;
    int& ref = a; // 引用
    int* ptr = &a; // 指针
    

  2. 引用在定义时必须初始化,指针可以不初始化
    int a = 10;
    int& ref = a; // 必须初始化
    int* ptr; // 可以不初始化
    ptr = &a; // 之后再初始化
    

  3. 引用一旦初始化后不能再改变引用对象,指针可以随时指向其他对象
    int a = 10;
    int b = 20;
    int& ref = a;
    // ref = b; // 错误,引用不能改变对象
    int* ptr = &a;
    ptr = &b; // 可以改变指向
    

  4. 没有NULL引用,但有NULL指针
    int* ptr = nullptr; // NULL指针
    // int& ref = nullptr; // 错误,没有NULL引用
    

  5. sizeof引用和指针
    int a = 10;
    int& ref = a;
    int* ptr = &a;
    std::cout << sizeof(ref) << std::endl; // 输出变量类型的大小
    std::cout << sizeof(ptr) << std::endl; // 输出指针类型的大小(4或8字节)
    

  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
    int a = 10;
    int& ref = a;
    ref++; // a 的值变为 11int arr[3] = {1, 2, 3};
    int* ptr = arr;
    ptr++; // ptr 指向下一个元素
    

  7. 有多级指针,但没有多级引用
    int a = 10;
    int* ptr = &a;
    int** pptr = &ptr; // 多级指针
    // int&& ref = a; // 没有多级引用
    

  8. 访问实体方式不同
    int a = 10;
    int& ref = a;
    int* ptr = &a;ref = 20; // 直接使用引用
    *ptr = 20; // 显式解引用
    

  9. 引用比指针使用起来更安全
  • 引用在初始化后不能变更,使得引用在使用上比指针更安全。

7. 内联函数

7.1 概念

内联函数是使用 inline 关键字修饰的函数。在编译时,C++编译器会在调用内联函数的地方直接展开函数体,而不是进行正常的函数调用。这避免了函数调用时建立栈帧的开销,从而提升程序运行的效率。

例子:

#include <iostream>// 定义内联函数
inline int Add(int a, int b) {return a + b;
}int main() {int result = Add(3, 4); // 调用内联函数std::cout << "结果: " << result << std::endl;return 0;
}

7.2 特性

  1. 以空间换时间

    • 概念:内联函数用函数体替换函数调用,省去调用的开销,但会使目标文件变大。
    • 缺点:目标文件变大,因为每次调用内联函数都会复制函数体。
    • 优点:减少函数调用的开销,提高运行效率。
  2. 编译器建议

    例子:

    inline void SmallFunction() {// 函数体很小,适合内联
    }
    

    3.声明和定义不分离

    • 概念inline 对编译器来说只是建议,不同编译器对 inline 的实现机制不同。
    • 建议使用场景:将小规模、非递归、且频繁调用的函数使用 inline 修饰。长函数或递归函数不适合使用 inline,编译器可能会忽略 inline

概念:内联函数不建议将声明和定义分离,否则可能导致链接错误。

原因:内联函数在编译阶段展开,不会生成函数地址,链接阶段找不到函数地址会报错。

错误例子:

// header.h
inline int Add(int a, int b); // 声明// source.cpp
inline int Add(int a, int b) { // 定义return a + b;
}// main.cpp
#include "header.h"
int main() {Add(3, 4);return 0;
}

正确例子:

// header.h
inline int Add(int a, int b) {return a + b;
}// main.cpp
#include "header.h"
int main() {Add(3, 4);return 0;
}

7.3 内联函数的使用建议

  • 内联函数适合用在短小且频繁调用的函数上,可以减少函数调用的开销。
  • 不适合将大函数和递归函数设为内联,因为这会增加代码体积并可能导致编译器忽略 inline 关键字。
  • 内联函数通常在头文件中定义,因为内联函数在编译阶段展开,需要在每个调用的地方都能看到函数体。

8. auto 关键字

8.1 类型别名思考

随着程序的复杂度增加,程序中用到的类型也变得越来越复杂,导致以下问题:

  1. 类型难于拼写:例如,std::map<std::string, std::string>::iterator 是一个非常长的类型名,很容易写错。
  2. 含义不明确导致容易出错:复杂的类型名可能会导致理解上的混淆。

聪明的程序员想到,可以使用 typedef 给类型取别名,例如:

typedef std::map<std::string, std::string>::iterator MapIterator;

虽然 typedef 可以简化代码,但它也有一些缺点,尤其是当我们需要根据表达式的类型来声明变量时,可能并不容易知道表达式的类型。

8.2 auto 简介

在早期的 C/C++ 中,auto 表示局部变量的自动存储类型,但几乎没人使用它。

在 C++11 中,auto 被赋予了新的含义:它不再是存储类型指示符,而是类型指示符。使用 auto 声明的变量由编译器在编译期推导其实际类型。

注意: 使用 auto 定义变量时,必须对其进行初始化,以便编译器推导其实际类型。

例子:

#include <iostream>
#include <map>
#include <string>int main() {std::map<std::string, std::string> myMap;myMap["hello"] = "world";// 使用 auto 简化代码auto it = myMap.begin();std::cout << it->first << ": " << it->second << std::endl;return 0;
}

8.3 auto 的使用细则

1.auto 与指针和引用结合使用

auto 声明指针类型时,用 autoauto* 没有区别,但用 auto 声明引用类型时必须加 &

int a = 10;
auto ptr = &a;    // ptr 是 int*
auto& ref = a;    // ref 是 int&std::cout << *ptr << ", " << ref << std::endl;

2.在同一行定义多个变量

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将报错,因为编译器只对第一个变量进行类型推导。

auto x = 10, y = 20;  // 正确,x 和 y 都是 int
// auto a = 10, b = 3.14;  // 错误,a 是 int,b 是 double,类型不同

8.4 auto 不能推导的场景

1.auto 不能作为函数的参数

// void func(auto x);  // 错误,不能使用 auto 作为函数参数

2.auto 不能直接用来声明数组

// auto arr[10];  // 错误,不能直接用 auto 声明数组

3.为了避免与 C++98 中的 auto 混淆,C++11 只保留了 auto 作为类型指示符的用法

4.auto 最常见的优势用法是与 C++11 提供的新式 for 循环和 lambda 表达式配合使用

std::vector<int> vec = {1, 2, 3, 4, 5};// 使用 auto 与新式 for 循环
for (auto& val : vec) {std::cout << val << " ";
}
std::cout << std::endl;

9. 基于范围的 for 循环

9.1 范围 for 的语法

在 C++98 中,如果要遍历一个数组,可以按照以下方式进行:

int arr[] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; ++i) {std::cout << arr[i] << std::endl;
}

C++11 引入了基于范围的 for 循环,使得遍历更加简单。for 循环后的括号由冒号 : 分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

例子:

#include <iostream>
#include <vector>int main() {std::vector<int> vec = {1, 2, 3, 4, 5};// 基于范围的 for 循环for (const auto& val : vec) {std::cout << val << " ";}std::cout << std::endl;return 0;
}

注意: 可以用 continue 结束本次循环,用 break 跳出整个循环。

9.2 范围 for 的使用条件

  1. 循环迭代的范围必须是确定的

    对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供 beginend 的方法。

  2. 迭代的对象要实现 ++== 的操作

10. 指针空值 nullptr

10.1 C++98 中的指针空值

在 C/C++ 中,如果一个指针没有合法的指向,我们通常会将其初始化为 NULL

int* p = NULL;  // 或者 int* p = 0;

但是 NULL 实际上是一个宏定义,通常被定义为 0(void*)0,这可能导致一些问题。

例子:

void f(int);
void f(int*);f(NULL);  // 编译器可能会选择 f(int) 而不是 f(int*)

10.2 nullptr 简介

C++11 引入了新的关键字 nullptr,专门表示空指针值,以解决 NULL 的问题。

例子:

10.2 nullptr 简介
C++11 引入了新的关键字 nullptr,专门表示空指针值,以解决 NULL 的问题。例子:

注意:

  1. 使用 nullptr 表示指针空值时,不需要包含头文件,因为 nullptr 是 C++11 引入的关键字。
  2. 在 C++11 中,sizeof(nullptr)sizeof((void*)0) 所占的字节数相同。
  3. 为了提高代码的健壮性,建议在表示指针空值时使用 nullptr

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/332714.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

神经网络不确定性综述(Part II)——Uncertainty estimation_Single deterministic methods

相关链接&#xff1a; 神经网络不确定性综述(Part I)——A survey of uncertainty in deep neural networks-CSDN博客 神经网络不确定性综述(Part II)——Uncertainty estimation_Single deterministic methods-CSDN博客 神经网络不确定性综述(Part III)——Uncertainty est…

Docker-Android安卓模拟器本地部署并实现远程开发测试

文章目录 1. 虚拟化环境检查2. Android 模拟器部署3. Ubuntu安装Cpolar4. 配置公网地址5. 远程访问小结 6. 固定Cpolar公网地址7. 固定地址访问 本文主要介绍如何在Ubuntu系统使用Docker部署docker-android安卓模拟器&#xff0c;并结合cpolar内网穿透工具实现公网远程访问本地…

用常识滚雪球:拼多多的内生价值,九年的变与不变

2024年5月22日&#xff0c;拼多多公布了今年一季度财报&#xff0c;该季度拼多多集团营收868.1亿元&#xff0c;同比增长131%&#xff0c;利润306.0亿&#xff0c;同比增长了202%&#xff0c;数据亮眼。 市场对拼多多经历了“看不见”、“看不懂”、“跟不上”三个阶段。拼多多…

STM32无源蜂鸣器播放音乐

开发板&#xff1a;野火霸天虎V2 单片机&#xff1a;STM32F407ZGT6 开发软件&#xff1a;MDKSTM32CubeMX 文章目录 前言一、找一篇音乐的简谱二、确定音调三、确定节拍四、使用STM32CubeMX生成初始化代码五、代码分析 前言 本实验使用的是低电平触发的无源蜂鸣器 无源蜂鸣器是…

【数据库】通过一个实例来认识数据流图DFD

导读&#xff1a;通过一个实例&#xff08;数据中台&#xff09;说明数据流图DFD的作用、介绍了常见的数据流图元素及其标准符号以及如何画数据流图。数据流图主要被分析师、系统设计师、流程优化专家、系统管理员以及与系统开发和维护相关的人员查看和使用。对于刚考完2024年5…

设计模式 18 迭代器模式 Iterator Pattern

设计模式 18 迭代器模式 Iterator Pattern 1.定义 迭代器模式 (Iterator Pattern) 是一种行为型设计模式&#xff0c;它提供了一种访问集合元素的标准方法&#xff0c;而无需暴露集合的内部表示。 提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而又不需要暴露该…

B树与B+树区别

B树和B树是常见的数据库索引结构&#xff0c;都具有相较于二叉树层级较少&#xff0c;查找效率高的特点&#xff0c;它们之间有以下几个主要区别&#xff1a; 1.节点存储数据的方式不同 B树的叶子结点和非叶子节点都会存储数据&#xff0c;指针和数据共同保存在同一节点中B树…

vue 引入 emoji 表情包

vue 引入 emoji 表情包 一、安装二、组件内使用 一、安装 npm install --save emoji-mart-vue二、组件内使用 import { Picker } from "emoji-mart-vue"; //引入组件<picker :include"[people,Smileys]" :showSearch"false" :showPreview&q…

YAML详情

一、kubernetes支持对象 Kubernetes支持YAML和JSON格式管理资源对象 JSON格式&#xff1a;主要用于api接口之间消息的传递YAML格式&#xff1a;用于配置和管理&#xff0c;YAML是一种简洁的非标记性语言&#xff0c;内容格式人性化&#xff0c;较易读 二、YAML语法格式注意点 …

微信小程序开发 tabbar组件常见问题

一、 tabbar不显示问题 问题 刚开始我在app.json中配置了下面的代码&#xff0c;但tabbar并没有显示。代码如下&#xff1a; "tabBar": {"custom": true,"color": "#7A7E83","selectedColor": "#3cc51f","…

C++ | Leetcode C++题解之第113题路径总和II

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<vector<int>> ret;unordered_map<TreeNode*, TreeNode*> parent;void getPath(TreeNode* node) {vector<int> tmp;while (node ! nullptr) {tmp.emplace_back(node->val);node …

单点登录与JWT

JWT:JSON Web Token JWT的作用是用户授权&#xff08;Authorization&#xff09;,而不是用户的身份认证&#xff08;Authentication&#xff09; 授权&#xff08;Authorization&#xff09;vs认证&#xff08;Authentication&#xff09; 用户认证指的是使用用户名、密码来…

pytest:指定测试用例执行顺序

在自动化测试中&#xff0c;测试用例的执行顺序有时对测试结果具有重要影响。本文将介绍如何在pytest框架中使用pytest-ordering插件以及Collection hooks来控制测试用例的执行顺序。 方式1&#xff1a; 使用pytest-ordering插件控制执行顺序 1.1 安装pytest-ordering插件 首…

XShell-连接-Centos 7

XShell 连接Centos 7 一.准备 安装XShell XShell下载地址&#xff1a; 在虚拟机上安装Centos 7&#xff0c;具体操作自行学习 二.Centos 7的准备 1.网络适配器修改为NAT 2.获取IP 输入命令&#xff1a; ip addr我的Centos 7对外IP为192.168.174.129 三.XShell连接Cento…

设计模式:适配器模式(Adapter)

设计模式&#xff1a;适配器模式&#xff08;Adapter&#xff09; 设计模式&#xff1a;适配器模式&#xff08;Adapter&#xff09;模式动机模式定义模式结构时序图模式实现在单线程环境下的测试在多线程环境下的测试模式分析优缺点适用场景应用场景应用实例适配器模式和代理模…

新书推荐:7.1 do while语句

本节必须掌握的知识点&#xff1a; 示例二十二 代码分析 汇编解析 ■do while语句其语法形式&#xff1a; do{ 语句块; }while(表达式) ■语法解析&#xff1a; ●执行do循环体内的语句块&#xff1b; ●判断while语句里的表达式&#xff0c;表达式为真继续下次循环&#…

上位机图像处理和嵌入式模块部署(f103 mcu运行freertos)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 mcu一般有两个用途,一个是控制,一个是非控制。控制类的应用一般要求实时性比较高,什么时候做什么,都是有严格的时间要求的。而非控制类的应用,则要求实现尽可能多的功能,比如…

PanTools v1.0.25 多网盘批量管理工具 批量管理、分享、转存、重命名、复制...

一款针对多个热门网盘的文件管理、批量分享、批量转存、批量复制、批量重命名、批量链接检测、跨账号移动文件、多账号文件搜索等&#xff0c;支持不同网盘的不同账号的资源文件操作。适用于网站站长、资源爱好者等&#xff0c;对于管理名下具有多个网盘多个账号具有实用的效果…

2024.05.26 第 399 场周赛

Leetcode 第 399 场周赛 优质数对的总数 I Leetcode 优质数对的总数 I 给你两个整数数组 nums1 和 nums2&#xff0c;长度分别为 n 和 m。同时给你一个正整数 k。 如果 nums1[i] 可以被 nums2[j] * k 整除&#xff0c;则称数对 (i, j) 为 优质数对&#xff08;0 < i < n…

React自定义Componment和State深层次理解-07

本节主要从底层原理上分析下React开发相关的内容和注意事项&#xff0c;本节会围绕使用展开&#xff0c;而非源码讲解。 Componment详解 什么是组件 在 MVVM架构出现之前&#xff0c;组件主要分为两种。 狭义上的组件&#xff0c;又称为 UI 组件&#xff0c;比如 Tabs 组件、…