【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.mzph.cn/news/841912.shtml

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

相关文章

Python并发编程大揭秘:打造你的多任务处理超能战队!

今天给大家带来的是学习如何构建一个能够同时处理海量任务的超级团队。从简单的线程和锁&#xff0c;到复杂的异步IO和多进程部署&#xff0c;每一个工具都像是你团队中的一员&#xff0c;各有所长&#xff0c;共同协作&#xff01; 文章目录 Python进阶之并发和并行编程详解1.…

【Linux-INPUT输入的子系统】

Linux-INPUT输入的子系统 ■ input 子系统简介■ input 驱动编写流程■ ■ input 子系统简介 input 子系统就是管理输入的子系统&#xff0c; input 子系统分为 input 驱动层、 input 核心层、 input 事件处理层&#xff0c;最终给用户空间提供可访问的设备节点 ■ input 驱…

React类组件生命周期详解

在React的类组件中&#xff0c;从组件创建到组件被挂载到页面中&#xff0c;这个过程react存在一系列的生命周期函数&#xff0c;最主要的生命周期函数是componentDidMount、componentDidUpdate、componentWillUnmount 生命周期图例如下 1. componentDidMount组件挂载 如果你…

S1E45:单链表1 课后作业

测试题&#xff1a;0. 相比起数组来说&#xff0c;单链表具有哪些优势呢&#xff1f; 答&#xff1a;长度非固定&#xff0c;可以申请添加长度 答案&#xff1a;对于数组来说&#xff0c;随机插入或者删除其中间的某一个元素&#xff0c;都是需要大量的移动操作&#xff0c;而…

网络攻击攻击之-远程命令执行/RCE告警运营分析篇

在各种网络安全产品的告警中,远程命令执行是一种非常常见的告警。本文将从远程命令执行的定义,远程命令执行利用的流量数据包示例,远程命令执行的suricata规则,远程命令执行的告分析警研判,远程命令执行的处置建议等几个方面阐述如何通过IDS/NDR,态势感知等流量平台的远程…

深入解析RPC技术:原理、实现与应用

RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;是一种计算机通信协议&#xff0c;允许一个程序&#xff08;客户端&#xff09;在本地调用另一个程序&#xff08;服务器&#xff09;中的函数或方法&#xff0c;并获取返回结果&#xff0c;就像调用…

C++:STL简介和容器string用法篇

一、STL简介 STL是C中的标准模板库&#xff08;Standard Template Library&#xff09;的缩写。它是C标准库的一部分&#xff0c;提供了一系列的数据结构和算法模板&#xff0c;包括各种容器、算法、迭代器、仿函数等&#xff0c;用于简化和加速C程序的开发过程。STL的设计理念…

shell脚本开发基础

shell脚本开发基础 什么是linux内置命令&#xff1f;什么是外置命令 内置命令&#xff1a;在系统启动时就加载入内存&#xff0c;常驻内存&#xff0c;执行效率更高&#xff0c;但是占用资源&#xff0c;cd 外置命令&#xff1a;系统需要从硬盘中读取程序文件&#xff0c;再读…

第八篇【传奇开心果系列】Python微项目技术点案例示例:以微项目开发为案例,深度解读Dearpygui 编写图形化界面桌面程序的优势

传奇开心果博文系列 系列博文目录Python微项目技术点案例示例系列 博文目录前言一、开发图形化界面桌面程序的优势介绍二、跨平台特性示例代码和解析三、高性能特性示例代码和解析四、简单易用特性示例代码和解析五、扩展性强示例代码和解析六、现代化设计示例代码和解析七、知…

什么是云渗透测试?

推荐阅读&#xff1a; 什么是安全态势&#xff1f; 什么是人肉搜索 什么是恶意软件&#xff1f; 什么是数字取证&#xff1f; 什么是语音网络钓鱼&#xff1f; 什么是网络安全中的社会工程&#xff1f; 什么是网络安全中的威胁情报&#xff1f; 什么是端点检测和响应 (…

linux 阿里云服务器安装ImageMagick和php扩展imagick

操作系统版本 Alibaba Cloud Linux 3.2104 LTS 64位 # 1.安装ImageMagick yum install -y ImageMagick ImageMagick-devel # 没有pecl要先安装pecl 和头文件 sudo yum install php-devel # 2.pecl 安装扩展 pecl install imagick #寻找所有php.ini文件 find / -name php.…

静态随机存储器(SRAM)

目录 介绍 基本的 SRAM 存储单元阵列 1. SRAM 存储单元 2. SRAM 阵列 3. SRAM 阵列的读写操作 4. SRAM 阵列的扩展 5. SRAM 阵列的应用 6. SRAM 阵列的优缺点 基本的 SRAM 逻辑结构 1. 存储单元 2. 存储单元阵列 3. 译码器 4. 读写电路 5. 控制逻辑 6. SRAM 逻辑…

【前端之ES6语法】

前端之ES6语法 1. ES6简介2. ES6新特性3.ES6不支持&#xff0c;使用babel3.1 参考文献 4.let和const命令5. 模版字符串6.函数之默认值、剩余参数7. 函数之扩展运算符、箭头函数8.箭头函数this指向和注意事项9.解构赋值10.对象扩展11.Symbol类型12.Set集合类型13.Map数据类型14.…

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

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

Java计算日期相差天数的几种方法

Java计算日期相差天数的几种方法 &#x1f5d3;️ Java计算日期相差天数的几种方法摘要引言一、使用java.util.Date和java.util.Calendar&#x1f4c5;1. 使用java.util.Date示例代码 2. 使用java.util.Calendar示例代码 二、使用java.time.LocalDate&#x1f4c6;示例代码 三、…

微信小程序文本框输入显示已经输入的字数

我们遇到这样的需求&#xff0c;就是微信小程序的输入框下面需要显示输入的字数&#xff1a; 我们通常会使用bindinput事件&#xff0c;让显示的字数等于value的长度&#xff0c;看下面的图&#xff1a; 但在实践中&#xff0c;真机测试中&#xff0c;我们会发现以下问题: 这个…

IP编址、进制转换、IP地址分类、变长子网掩码VLSM、无类域间路由CIDR

前言 网络层位于数据链路层与传输层之间。网络层中包含了许多协议&#xff0c;其中最为重要的协议就是IP协议。网络层提供了IP路由功能。理解IP路由除了要熟悉IP协议的工作机制之外&#xff0c;还必须理解IP编址以及如何合理地使用IP地址来设计网络。 IP编址 每个网段上都有两…

Java的类路径究竟是什么?

回答 问了chatgpt这个问题&#xff0c;首先类路径的定义是&#xff1a; 是指一组路径&#xff0c;这些路径告诉Java虚拟机&#xff08;JVM&#xff09;和类加载器在哪里可以找到应用程序所需的类和资源文件。说白了就是在运行java程序的时候需要先将java源代码编译成class文件…

基础IO用户缓冲区 、inode、硬软链接【Linux】

文章目录 用户缓冲区磁盘磁盘分区EXT2文件系统的存储方案 inode软链接硬链接 用户缓冲区 代码一&#xff1a; 1 #include<stdio.h>2 #include<unistd.h>3 #include<string.h> 4 int main()5 {6 const char * fstr &…

基于FIDO2和USBKEY硬件的SSH认证

在 8.2&#xff08;最新为 8.3&#xff09;版本中&#xff0c;OpenSSH 提供了对 FIDO 和 UAF 的支持。从此用户就可以用硬件 USBKEY 证书进行 SSH 原生认证。这样可以实现简捷、有效和安全的 SSH 认证。本文我们就就少一下 FIDO2 以及 OpenSSH 对其的支持&#xff0c;并尝试一下…