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

news2024/11/17 12:44:45

 

 

目录

​编辑

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::endl

namespace 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;              // 从键盘读取输入到变量number
    std::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; // 输出 10
    return 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 的值变为 11
    
    int 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.coloradmin.cn/o/1699996.html

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

相关文章

2024年5月27日 十二生肖 今日运势

小运播报&#xff1a;2024年5月27日&#xff0c;星期一&#xff0c;农历四月二十 &#xff08;甲辰年己巳月辛卯日&#xff09;&#xff0c;法定工作日。 红榜生肖&#xff1a;羊、蛇、狗 需要注意&#xff1a;鼠、鸡、龙 喜神方位&#xff1a;西南方 财神方位&#xff1a;…

vue3 vite项目配置了proxy代理情况下查看真实的接口调用地址

vite配置了proxy代理情况下如何查看真实的接口调用地址? 使用vite进行代理 在vite.config.ts配置了代理 在浏览器查看请求头和响应头发现只有代理前的url&#xff0c;没有显示代理后的路径 然后发现一个bypass函数&#xff0c;但是此函数只能修改res响应头的数据&#xff0…

指纹识别系统架构

目录 1. 系统架构 1.1 指纹采集模块 1.2 指纹处理模块 1.3 指纹登记模块 1.4 指纹识别模块 1.5 指纹识别决策模块 1.6 管理模块 1.6.1 存储管理 1.6.2 传输管理 1.6.3 安全管理 1.7 应用开放功能 1.7.1 指纹登记功能 1.7.2 指纹验证功能 1.7.3 指纹辨识功能 2. …

DM Hw6

Hw6 聚类 1ab 2abcd 3abcde 456789 1 a b 一个点不来自某个特定簇的概率是 1 − 1 K 1-\frac{1}{K} 1−K1​ 对所有 2 K 2K 2K 个点都不来自该簇的概率是 ( 1 − 1 K ) 2 K (1-\frac{1}{K})^{2K} (1−K1​)2K 则 至少一个点来自该簇的概率为 1 − ( 1 − 1 K ) 2 K 1-(1-…

vs2019 c++20 规范的 STL 库的智能指针 shared、unique 、weak 及 make_** 函数的源码注释汇总,和几个结论

智能指针的源码都在 《memory》 头文件中。因为头文件太长&#xff0c;再者本次整理是基于以前的零散的模板分析。故相当于抽取了该头文件中关于智能指针的源码进行分析&#xff0c;注释。 &#xff08;1 探讨一&#xff09;当独占指针指向数组时&#xff0c;其默认的删除器是…

二叉树——经典练习题

目录 前言&#xff1a; 一、单值二叉树 题目描述&#xff1a; 思路分析&#xff1a; 代码实现&#xff1a; 二、二叉树最大深度 题目描述&#xff1a; 思路分析&#xff1a; 代码实现&#xff1a; 三、检查两颗树是否相同 题目描述&#xff1a; 思路分析&#xff1a; 代…

EXCEL怎么计算一列数据里的最大连续重复次数?

有这样一列数据&#xff0c;怎么用EXCEL数据求出&#xff0c;重复出现的单/双的次数的最大值。 这里演示使用辅助列数据透视表。 一、单/双出现的次数的最大值 这个可不用辅助列&#xff0c;选中数据透视表将数据 单/双列放在行字段&#xff0c;和值字段&#xff0c;值字段会…

netplan

文章目录 前言官网配置文件默认配置NetworkManager 简介systemd-networkd 简介NetworkManager VS systemd-networkdNetplan 简介示例多个配置文件 前言 netplan 是 ubuntu 17.10 版本中引入的一种全新的命令行网络配置程序&#xff0c;用于在 ubuntu 系统中轻松管理和配置网络…

C语言 | Leetcode C语言题解之第114题二叉树展开为链表

题目&#xff1a; 题解&#xff1a; void flatten(struct TreeNode* root) {struct TreeNode* curr root;while (curr ! NULL) {if (curr->left ! NULL) {struct TreeNode* next curr->left;struct TreeNode* predecessor next;while (predecessor->right ! NULL)…

【DevOps】Jenkins + Dockerfile自动部署Maven(SpringBoot)项目

环境 docker_host192.168.0.1jenkins_host192.168.0.2 jenkins_host构建完成后把jar发布到docker_host&#xff0c;再通过dockerfile自动构建镜像&#xff0c;运行镜像 1 Jenkins安装 AWS EC2安装Jenkins&#xff1a;AWS EC2 JDK11 Jenkins-CSDN博客 AWS EC2上Docker安装…

此物一出天下反,Dora犹如优秀框架的粘合剂

引言&#xff1a;千里之行&#xff0c;始于足下。 如果你从事Android开发&#xff0c;请认真看完本篇文章&#xff0c;因为可能会颠覆你对Android开发的认识。 当夜空中繁星点点&#xff0c;一颗璀璨的流星划过&#xff0c;其辉光洒在古老的山谷之中&#xff0c;照亮了一个隐藏…

banner2.0自定义轮播布局

说明&#xff1a;最近碰到一个需求&#xff0c;让新闻列表实现轮播图的效果&#xff0c;也就是轮播新闻&#xff0c;然后样式必须按照ui设计的样式来弄&#xff0c;之前传统的banner&#xff0c;都是只轮播图片&#xff0c;没想到&#xff0c;这次居然要轮播新闻&#xff0c; 网…

夏老师小课堂(7) 免费撸Harmony0S应用开发者高级认证

点击上方 “机械电气电机杂谈 ” → 点击右上角“...” → 点选“设为星标 ★”&#xff0c;为加上机械电气电机杂谈星标&#xff0c;以后找夏老师就方便啦&#xff01;你的星标就是我更新动力&#xff0c;星标越多&#xff0c;更新越快&#xff0c;干货越多&#xff01; 关注…

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 …

高效的大型语言模型适应方法:提升基础性的解决方案

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

C++系列-static成员

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 概念 声明为static的类成员称为类的静态成员&#xff0c;用static修饰的成员变量&#xff0c;称之为静态成员变量&#xff0c;用static修饰的成员函数&#xff0c;称之为静态成…

Django框架css文件能正常加载,但是css样式不生效

最近运行一个Django项目&#xff0c;能正常启动运行&#xff0c;css文件也能够正常加载&#xff0c;但是css样式却没有正常渲染。 解决办法&#xff1a; 1、打开注册表&#xff1a;winR 2、找到&#xff1a;计算机\HKEY_CLASSES_ROOT\.css 修改&#xff1a;Content Type 值&…

【机器学习结合AI绘画工具】——开启艺术创作的新纪元

目录 一、AI绘画工具的发展历程 二、AI绘画工具的技术原理 实例说明 三、AI绘画工具在艺术创作中的应用 实例网站 四、AI绘画工具的影响与未来展望 结论 机器学习和人工智能&#xff08;AI&#xff09;在过去的十年里取得了显著的进展。特别是在艺术创作领域&#xff0c…

linux创建离线yum源给局域网机器使用

适用场景&#xff1a;在封闭的内网环境中&#xff0c;无法使用互联网进行安装各种rpm包的时候&#xff0c;离线yum源可以解决大部分问题&#xff0c;配置号后可直接使用yum进行安装包 1.准备好镜像源ISO&#xff1a; 例如以下示例&#xff0c;具体可参考自己的系统进行下载&a…

50-Qt控件详解:Input Display

#ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> //1.Combo Box控件 #include<QComboBox> //2.QFontComboBox控件 #include<QFontComboBox> #include<QLabel>//3.Line Edit控件 #include<QLineEdit> #include <QPushButton…