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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

部署PIM-SM

拓扑图 配置 使能组播路由 配置OSPF 组播路由器接口配置pim-sm 连接组成员的接口使能igmp pim路由器上配置静态RP sysname AR1 # multicast routing-enable # interface GigabitEthernet0/0/0ip address 10.1.12.1 255.255.255.0 pim sm # interface GigabitEthernet0/0/…

SpringBoot + MybatisPlus

SpringBoot MybatisPlus 整合记录 1. 硬件软件基本信息2. 相关链接3. 通过idea快速生成一个Springboot项目4. 启动报错问题解决问题一&#xff1a;Springboot启动的时候报错提示 “没有符合条件的Bean关于Mapper类型”问题二&#xff1a;启动的时候提示需要一个Bean&#xff0…

文件操作知识点

前言: 我们应该知道一般程序运行时产生的数据是存放在内存中的。但是如果程序关闭后这些内存就会被系统回收&#xff0c;如果内存内的有用的数据没有被保存下来&#xff0c;这些数据就丢失了。所以这个时候我们就可以使用磁盘来储存我们的数据。 目录 程序文件的分类 文件名…

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

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

Android 快速调试网络 复制curl 到postMan

搜索这个插件 官网地址&#xff1a;https://github.com/itkacher/OkHttpProfiler 集成教程也在里面集成完毕后右下角有一个入口点进去可以复制curl| 插件名称&#xff1a;Okhttp Profiler 真的很好用&#xff01;

软测刷题-错题1

提高测试效率的方法&#xff1a; 1、不要做无效的测试 2.不要做重复的测试 3.不同测试版本的测试侧重点 4.优化测试顺序 LoadRunner是对服务器进行施压。 在数据库中存在的用户数是指注册用户数。 input标签可以直接使用send_keys实现上传&#xff0c;而非input标签是无法直…

指纹识别系统架构

目录 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. …

SparkStreaming架构原理(详解)

Spark概述 SparkStreaming架构原理 Spark Streaming的架构主要由以下几个关键部分组成。 1.数据源接收器&#xff08;Receiver&#xff09; 执行流程开始于数据源接收阶段&#xff0c;其中接收器&#xff08;Receiver&#xff09;负责从外部数据源获取数据流。 接收器可以连…

Android Studio开发之路(十四)自定义Titlebar以及设置顶部状态栏颜色

一、描述 项目需求&#xff0c;我要做一个下图这样的titlebar,包括一个返回按钮&#xff0c;一个关闭按钮&#xff0c;一个文本框。默认的titlebar按钮设计不太满足我的需求&#xff0c;于是我打算自定义一个titlebar组件&#xff0c;应用到我的每一个页面 二、titlebar组件设…

flink程序本地运行:No ExecutorFactory found to execute the application

1.问题描述 在idea中运行flink job程序出现如下错误&#xff1a; Exception in thread "main" java.lang.IllegalStateException: No ExecutorFactory found to execute the application. at org.apache.flink.core.execution.DefaultExecutorServiceLoader.getE…

【蓝桥杯】国赛普及-

题目列表 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) P9420 [蓝桥杯 2023 国 B] 子 2023 / 双子数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include<bits/stdc.h> using llunsigned long long; #define int ll const int N2e510; int k0; std::string s; int…

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

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

HTML橙色爱心

目录 写在前面 准备开始 完整代码 运行结果 系列文章 写在后面 写在前面 本期小编给大家分享一颗热烈且浪漫的爱心&#xff0c;快来看看吧&#xff01; 准备开始 在开始之前&#xff0c;我们需要先简单的了解一下这颗爱心的原理哦~ 本期将用html实现这颗跳动的爱心&a…

0基础认识C语言

为了给0基础一个舒服的学习路径&#xff0c;就有了这个专栏希望带大家一起进步。 话不多说&#xff0c;开始正题。 一、C语言的一段小历史 C语言的设计要追溯到20世纪60年代末和70年代初&#xff0c;在那个时代美国有这么一号人叫做丹尼斯.里奇&#xff0c;他和同事肯.汤普逊…

redis数据操作相关命令

1.list操作 1.1 rpush rpush&#xff1a;新的元素添加到list最右边 #从右边依次往List添加1,2,3 RPUSH name 1 RPUSH name 2 RPUSH name 3#查看列表&#xff1a;返回 1,2,3 LRANGE name 0 -1结果如下&#xff1a; 1.2 lpush lpush&#xff1a;新加的元素在list最左边 #从…

WordPress安装插件失败No working transports found

1. 背景&#xff08;Situation&#xff09; WordPress 社区有非常多的主题和插件&#xff0c;大部分人用 WordPress 都是为了这些免费好用的主题和插件。但是今天安装完 WordPress 后安装插件时出现了错误提示&#xff1a;“ 安装失败&#xff1a;下载失败。 No working trans…

Android 处理音频焦点,解决音乐播放冲突的问题

1. 音频焦点是什么 在Android中&#xff0c;两个或多个 Android 应用可以同时将音频播放到同一输出流&#xff0c;系统会将所有音频混合在一起。 但是多数情况下&#xff0c;这对于用户来说是感到困惑的。为了避免多个应用的多个音频一起播放&#xff0c;Android 引入了“音频…

HTML5 基本框架

HTML5基本的内容 文章目录 系列文章目录前言一、HTML5 基本框架二、具体框架结构三、知识补充总结 前言 HTML5的介绍&#xff1a; HTML5 是一种用于构建网页内容的标准化语言。它是 HTML&#xff08;超文本标记语言&#xff09;的第五个版本&#xff0c;引入了许多新的功能和特…

视频号小店去哪里找货源?最全货源渠道分享!

大家好&#xff0c;我是电商糖果 视频号小店因为是这两年电商行业新出来的黑马&#xff0c;吸引着不少商家入驻。 入驻了商家中很多都没有自己的货源渠道。 他们基本都是从无货源开始起步&#xff0c;后期通过积累资源&#xff0c;慢慢搭建属于自己的货源渠道。 可是渐渐的…

Oracle实践|内置函数之聚合函数

&#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端工程师 &#x1f3c6; 近期荣誉&#xff1a;华为云云享专家、阿里云专家博主、腾讯云优秀创作者、ACDU成员 &#x1f525; 三连支持&#xff1a;欢迎 ❤️关注…