新特性之C++17

目录

u8字符字面量
noexcept作为类型系统的一部分
Lambda表达式捕获*this
constexpr新特性
编译期的if constexpr语句
constexpr的Lambda表达式
变量新特性
inline变量
结构化绑定
if和switch语句中的初始化器
强制的复制省略(返回值优化)
临时物质化
模板新特性
折叠表达式(…)
类模板实参推导
auto占位的非类型模板形参

u8字符字面量

C++17 引入了u8字符字面量,用于表示 UTF-8 编码的字符串。

auto str = u8"Hello, 世界";

noexcept作为类型系统的一部分

noexcept是 C++11 引入的一个关键字,用于改善C++中异常处理的性能和可用性。noexcept指定的函数保证不会抛出异常,这使得编译器有机会进行优化,同时也为程序员提供了一个清晰的工具来指明哪些函数是安全的,即不会因为异常而失败。
C++17对其改动主要如下:

  1. 更广泛的使用

C++17 标准库在很多已有的函数中增加了noexcept说明。这是因为对异常安全性有更高的要求和对性能优化的关注。比如,在移动语义和智能指针等方面,更频繁地看到noexcept的使用。

  1. 推导规则

C++17 引入了新的推导指南,使得noexcept能够在模板和自动类型推导中得到更好的处理。这包括在模板函数和自动返回类型中,noexcept的状态可以被推导出来。例如,一个模板函数可以根据其模板参数的操作是否不抛出异常,来决定自身是否声明为noexcept

  1. 移动操作的默认noexcept

在 C++11 和 C++14 中,移动构造函数和移动赋值操作不会自动被推断为noexcept,而在 C++17 中,如果一个类的所有成员和基类的移动构造函数和移动赋值操作都是noexcept的,那么这个类的移动操作也会被推断为noexcept。这改善了容器(如 std::vector)在元素类型是可移动但不抛出异常时的性能,因为容器可以安全地进行更优化的内存操作,如使用realloc而不是手动复制。

  1. 对std::swap的优化

C++17 中,std::swap在可能的情况下使用noexcept来确保不抛出异常,这对于某些类型来说,特别是在模板编程中,可以提高效率和安全性。

Lambda表达式捕获*this

C++17的Lambda引入捕获*this,使得可以捕获当前对象的常量副本,相当于是以值捕获的形式捕获了this指向的对象,并且赋予const属性。

class A
{
    int a = 1;
public:
    void printCopyA()
    {
        auto lambda = [*this] {
            a++; // error C3490: 由于正在通过常量对象访问“a”,因此无法对其进行修改
            std::cout << a;
        };
        lambda();
    }

    void printA()
    {
        auto lambda = [&] {
            a++;
            std::cout << a;
        };
        lambda();
    }
};

int main()
{
    A a;
    a.printA();
    a.printCopyA();
}

constexpr新特性

编译期的if constexpr语句

C++17引入if constexpr语句,这是一个在编译时决定条件分支的语言特性。主要是为了增强模板和编译时多态的功能,使得基于模板参数的条件编译变得更为直接和清晰。

#include <iostream>
#include <type_traits>

template<typename T>
void process(T value) {
    if constexpr (std::is_integral_v<T>) {
        std::cout << "Integral type with value: " << value << std::endl;
    } else if constexpr (std::is_floating_point_v<T>) {
        std::cout << "Floating-point type with value: " << value << std::endl;
    } else {
        std::cout << "Other type" << std::endl;
    }
}

int main() {
    process(10);    // 输出:Integral type with value: 10
    process(3.14);  // 输出:Floating-point type with value: 3.14
    process("Hello");  // 输出:Other type
}

特点和限制

  • 编译时求值if constexpr的条件必须能在编译时得到求值。
  • 作用域限制:只有符合条件的分支会被编译,这意味着在不符合条件的分支中的代码即使含有编译错误,也不会引起编译失败,因为这部分代码根本不会被编译。

例如下面代码,在else分支下调用不存在的函数,编译不会失败:

#include <iostream>
#include <type_traits>

template<typename T>
void process(T value) {
    if constexpr (std::is_integral_v<T>) {
        std::cout << "Integral type with value: " << value << std::endl;
    } else if constexpr (std::is_floating_point_v<T>) {
        std::cout << "Floating-point type with value: " << value << std::endl;
    } else {
        nonExistentFunc(value); // 不会被编译
        std::cout << "Other type" << std::endl;
    }
}

int main() {
    process(10);    // 输出:Integral type with value: 10
    process(3.14);  // 输出:Floating-point type with value: 3.14
}

constexpr的Lambda表达式

C++17 允许Lambda表达式在constexpr上下文中使用,从而使其可以在编译期求值。

constexpr auto add = [](int a, int b) { return a + b; };
static_assert(add(2, 3) == 5);

变量新特性

inline变量

C++17 引入的内联变量是一个重要的语言特性,它主要解决了多个编译单元之间共享全局变量的链接问题,特别是对于模板和头文件中定义的变量。在此之前,全局变量或静态成员变量的定义可能会导致多个定义问题(One Definition Rule,ODR)的违规,尤其是在涉及到头文件中包含的变量时。

内联变量的用途

  1. 解决多个定义问题(ODR):在 C++ 中,非内联变量在多个源文件中定义时,会违反 ODR,导致链接错误。内联变量允许在多个编译单元中定义同一个变量,编译器保证在程序中只有一个实例。
  2. 简化模板和头文件中的变量定义:C++17 之前,如果在头文件中定义静态成员变量或全局变量,通常需要在一个源文件中提供该变量的定义以避免链接时的重复定义问题。内联变量允许在头文件中直接定义并初始化变量,简化了代码结构。

语法

内联变量的声明非常直接,只需要在变量声明前加上 inline 关键字:

inline int myGlobalVar = 10;

结构化绑定

C++17 引入的结构化绑定(Structured Bindings)是一种新的语言特性,旨在提供一种简洁、直观的方式来解包(unpack)元组、结构体或数组中的数据到单独的变量中。这个特性极大地增强了代码的可读性和易用性,特别是在处理复合数据类型或从函数返回多个值时。

工作原理

结构化绑定允许你同时定义多个变量,将它们绑定到一个聚合数据类型(如元组、数组、结构体或配对)的各个成员上。这些变量可以被视作原始数据结构中对应成员的别名。

基本语法

结构化绑定的基本语法如下:

auto [x, y, z] = expression;

其中 expression 必须是一个返回聚合类型(如元组、结构体、数组)的表达式。x, y, z 等变量被创建为引用或值,这取决于表达式的类型和上下文。

使用场景

  • 从函数返回多个值: 使用结构化绑定,函数可以返回一个 std::tuple 或 std::pair,调用者可以非常直观地获取这些值。
  • 解包数组和元组: 直接将数组或元组的元素绑定到变量上,简化数组或元组的处理代码。
  • 访问结构体成员: 对于简单的 POD(Plain Old Data)类型的结构体,可以直接绑定到其成员上,而不需要逐一指定。

示例

  1. 元组解包
#include <tuple>
#include <iostream>

std::tuple<int, double, std::string> getTuple() {
    return {42, 3.14, "Hello"};
}

int main() {
    auto [a, b, c] = getTuple();
    std::cout << a << ", " << b << ", " << c << std::endl;  // 输出:42, 3.14, Hello
}
  1. 结构体解包
#include <iostream>

struct Point {
    int x, y;
};

int main() {
    Point p{10, 20};
    auto [x, y] = p;
    std::cout << x << ", " << y << std::endl;  // 输出:10, 20
}
  1. 数组解包
#include <iostream>

int main() {
    int arr[] = {1, 2, 3};
    auto [a, b, c] = arr;
    std::cout << a << ", " << b << ", " << c << std::endl;  // 输出:1, 2, 3
}

if和switch语句中的初始化器

C++17引入ifswitch语句的初始化器,使得在使用条件判断时可以在内部进行变量初始化。

if示例

#include <iostream>
#include <map>

int main() {
    std::map<std::string, int> myMap = {{"Alice", 5}, {"Bob", 10}};

    if (auto it = myMap.find("Alice"); it != myMap.end()) {
        std::cout << "Found Alice with score " << it->second << std::endl;
    } else {
        std::cout << "Alice not found" << std::endl;
    }
}

switch示例

#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers = {10, 20, 30, 40};

    switch (auto i = numbers.size(); i) {
        case 4:
            std::cout << "There are four elements." << std::endl;
            break;
        case 3:
            std::cout << "There are three elements." << std::endl;
            break;
        default:
            std::cout << "The number of elements is not 3 or 4." << std::endl;
    }
}

优点

  • 作用域控制:初始化的变量仅在 if 或 switch 语句块中有效,限制了变量的作用域,避免了不必要的作用域泄露。
  • 代码清晰和紧凑:通过将相关的初始化和条件判断放在一起,代码更加整洁,逻辑更清晰。
  • 避免前置声明:不需要在 if 或 switch 前面单独声明变量,减少了代码行数和复杂性。

强制的复制省略(返回值优化)

C++17 引入的强制的复制省略(Guaranteed Copy Elision)或者更准确地称作返回值优化(Return Value Optimization,RVO)的强化版本,是一个重要的编译器优化特性,它可以显著减少或消除某些情况下的对象复制和移动操作。这种优化不仅提高了程序的性能,还改善了资源管理,特别是在涉及到大型对象或者资源密集型对象时。

背景与问题

在 C++17 之前,返回局部对象时通常会涉及到复制或移动构造函数的调用,即使编译器应用了返回值优化(RVO)或命名返回值优化(NRVO)。但这些优化是可选的,不是强制的,这意味着编译器可以选择不进行这些优化,从而导致性能损失。

C++17 的改变

C++17 通过修改语言的核心规则(有时被称为“强制 RVO”或“保证复制省略”),确保在某些特定情况下消除这些复制和移动操作。这主要体现在两个方面:

  1. 当对象从函数返回时:C++17 要求编译器必须省略局部对象的复制和移动操作,直接在调用方的上下文中构造这些对象。
  2. 从表达式构造新对象时:例如在使用列表初始化或直接初始化对象时。

技术细节

具体来说,C++17 标准规定,如果返回的对象类型与函数返回类型相符,并且返回的是一个局部对象或临时对象,编译器必须省略复制或移动构造函数的调用,直接在接收对象的内存位置构造返回对象。

示例

以下示例展示了 C++17 中的强制复制省略如何工作:

#include <iostream>
#include <vector>

class BigObject {
public:
    std::vector<int> data;

    BigObject() {
        std::cout << "Constructor called" << std::endl;
    }

    BigObject(const BigObject&) {
        std::cout << "Copy constructor called" << std::endl;
    }

    BigObject(BigObject&&) noexcept {
        std::cout << "Move constructor called" << std::endl;
    }
};

BigObject createBigObject() {
    BigObject obj;
    obj.data.resize(1000);  // 假设是一个资源密集型操作
    return obj;
}

int main() {
    BigObject myObj = createBigObject();
    // 应该看不到复制或移动构造函数的调用信息
    return 0;
}

在 C++17 中运行这段代码时,你不会看到复制构造函数或移动构造函数被调用的信息,因为编译器直接在 myObj 的存储位置构造了 obj。

优点

  • 性能提升:避免不必要的复制和移动操作,特别是对于大对象或资源密集型对象。
  • 简化语义:程序员不需要依赖于编译器是否会应用(N)RVO来保证性能,因为现在这些优化是由语言规范保证的。

临时物质化(Temporary Materialication)

在 C++17 中引入了一个重要的概念:临时物质化(Temporary Materialization)。这个特性涉及到临时对象的创建,尤其是在需要对象实体时,如在传递参数、初始化、返回值等场景中。

背景

在早期的 C++ 标准中,临时对象(比如由表达式产生的未命名对象)的行为有时候可能会造成理解和使用上的混淆,特别是在它们与引用绑定、返回值和函数参数传递等方面。C++17 对这些规则进行了明确,确保临时对象的行为更加直观和可预测。

临时物质化的定义

临时物质化是指在需要一个完整的对象时,将一个临时的prvalue(纯右值)表达式转换为一个临时对象的过程。这主要发生在以下几种情况:

  1. 当 prvalue 需要作为引用的初始化值时: 如果一个 prvalue 被用作初始化一个引用,那么这个 prvalue 将会物质化为一个临时对象,以便引用可以绑定到它上面。
  2. 在 prvalue 作为函数参数传递时: 如果函数参数是按值传递的,而传递的实参是 prvalue,那么这个 prvalue 将物质化为一个临时对象,然后将其传递给函数。
  3. 在 prvalue 作为函数的返回值时: 当函数返回一个 prvalue 时,这个 prvalue 通常会物质化为一个临时对象,特别是在涉及到返回类型转换时。

示例

下面是一些示例,展示了 C++17 中临时物质化的具体应用:

#include <iostream>

struct A {
    int value;
    A(int v) : value(v) { std::cout << "A(" << value << ") constructed\n"; }
    A(const A& other) : value(other.value) { std::cout << "A copied\n"; }
};

A getA() {
    return A(5); // 返回 prvalue
}

void takeA(A a) {
    std::cout << "Received A: " << a.value << std::endl;
}

int main() {
    const A& aRef = A(10); // prvalue 物质化为临时对象,引用绑定到它
    takeA(A(20)); // prvalue 物质化为临时对象,传递给函数

    A myA = getA(); // prvalue 物质化过程
    return 0;
}

在这个例子中,每次 A(数字) 被调用时,都创建了一个 prvalue,随后在需要时物质化为一个临时对象。这些临时对象被用来初始化引用、作为函数参数,或直接赋值给变量。

模板新特性

折叠表达式(…)

在了解折叠表达式前先了解C++11引入的形参包,模板形参包 是一个与可变参数模板(Variadic Templates)紧密相关的概念。形参包允许你在函数或模板定义中接受不确定数量的模板参数或函数参数,使得模板和函数可以具有通用性和灵活性,能够处理任意数量和类型的输入参数。

语法

( 形参包 运算符... ) // 一元右折叠
( ...运算符 形参包 ) // 一元左折叠
( 形参包 运算符...运算符 初值 ) // 二元右折叠
( 初值 运算符...运算符 形参包 ) // 二元左折叠

其实一元和二元的概念是一样的,只是二元折叠要多一个初值参数。

示例说明

  1. 一元右折叠
#include <iostream>
#include <string>

template<typename... Args>
std::string concatenateRight(Args... args) {
    return (args + ... + std::string(""));  // 一元右折叠
}

int main() {
    std::cout << "Concatenate Right: " << concatenateRight("Hello", " ", "World", "!") 
        << std::endl; // 打印:Concatenate Right: Hello World!
}
  1. 一元左折叠
#include <iostream>
#include <string>

template<typename... Args>
std::string concatenateLeft(Args... args) {
    return (std::string("") + ... + args);  // 一元左折叠
}

int main() {
    std::cout << "Concatenate Left: " << concatenateLeft("Hello", " ", "World", "!") 
        << std::endl; // 打印:Concatenate Left: Hello World!
}
  1. 二元右折叠
#include <iostream>

template<typename... Args>
int sum(int init, Args... args) {
    return (args + ... + init); // 二元右折叠
}

int main() {
    std::cout << "Sum with initial 10: " << sum(10, 1, 2, 3) << '\n'; // 打印:16
}
  1. 二元左折叠
#include <iostream>

template<typename... Args>
int sum(int init, Args... args) {
    return (init + ... + args); // 二元左折叠
}

int main() {
    std::cout << "Sum with initial 10: " << sum(10, 1, 2, 3) << '\n'; // 打印:16
}

类模板实参推导

在C++17之前,当你使用模板类时,通常需要显式地指定所有的模板参数。例如,使用标准库中的std::pairstd::vector时,你需要这样写:

std::vector<int> v = {1, 2, 3};
std::pair<int, double> p = {1, 3.14};

但是有了类模板实参推导,你可以省略模板参数:

std::vector v = {1, 2, 3};  // 推导为 std::vector<int>
std::pair p = {1, 3.14};    // 推导为 std::pair<int, double>

auto占位的非类型模板形参

什么是非类型模板参数

非类型模板参数(Non-type template parameters)是C++模板编程中的一种强大的特性,允许在定义模板时使用一个具体的值而不是类型作为模板参数。这种参数可以是一个整数、枚举、指针或引用,甚至某些类的常量表达式,它们用于生成依赖于这些值的模板实例。

示例说明

template<auto Value>
class Constant
{
};

int main()
{
    std::vector<int> vv{1, 2, 3};
    // 使用例子
    Constant<5>    intConst;    // 推导为 Constant<int>
    Constant<'a'>  charConst;   // 推导为 Constant<char>
    //Constant<3.14> doubleConst; // 推导为 Constant<double>
}

上面示例代码中使用std::vector时传递的形参必须是一个类型,而不能是一个值;但使用Constant模板可以传递具体的值,因为它通过auto占位符来使编译期间自动推导出具体的类型。

上面示例中在使用double类型的值进行实例化时会编译失败,是因为在C++20前非类型模板形参不能是double类型。

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

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

相关文章

企业数据API平台:获取企业多维度信息

数据API平台是指提供一系列预先定义的接口、协议与工具&#xff0c;允许不同应用程序或系统之间进行数据交换和通信的平台。这些接口被称为数据API&#xff08;Data Application Programming Interface&#xff09;&#xff0c;是数据管理系统或应用程序提供的一组开放式接口。…

Linux手动安装JDK1.8

1、下载要安装的jdk安装包文件 官网下载地址&#xff1a;https://www.oracle.com/cn/java/technologies/downloads/ 2、上传jdk安装包至要安装服务器 3、在要安装jdk位置使用命令解压安装包 安装路径: /usr/local/java 解压安装包&#xff0c;解压命令 tar -zxvf /install…

【博客21】缤果Qt5仿小米耳机APP布局_PC端软件(高级篇)

小米耳机 备注&#xff1a;此软件只是简单的实现布局和界面跳转逻辑, 并未加入小米协议相关内容,因需要鉴权方式验证,故无法进行通讯编程. 开发工具: qt-opensource-windows-x86-5.14.2 (编程语言C) Android反编译工具: apktool 小米小爱开放平台 - 语音服务平台 - 文档中…

3-1 激活函数和神经网络思想

3-1 激活函数和神经网络思想 主目录点这里

android应用的持续构建CI(四)-- 依赖环境(兼容多版本的gradle和jdk)

一、背景 android应用的构建前提是&#xff0c;安装好了gradle和jdk。在实际使用的过程中&#xff0c;不同的android应用&#xff0c;对gradle和jdk的版本要求不一。 于是&#xff0c;在jenkins服务器上&#xff0c;我们需要安装多种版本的gradle和jdk。 安装过jdk的小伙伴应…

Jenkins 使用 Publish over SSH进行远程访问

Publish over SSH 是 Jenkins 的一个插件,可以让你通过 SSH 将构建产物分发到远程服务器。以下是如何开启 Publish over SSH 的步骤: 一、安装 Publish over SSH 插件 在 Jenkins 中,进入 "Manage Jenkins" > "Manage Plugins"。选择 "Availab…

【spring MVC的执行流程】

SpringMVC可以说是Servlet的封装&#xff0c;屏蔽了Servlet的很多细节&#xff0c;比如Servlet再获取参数的时候需要不停地getParameter,现在只要在SpringMVC方法定义对应的JavaBean&#xff0c;只要属性和参数名一致&#xff0c;SpringMVC就可以帮我们实现将参数封装到JavaBea…

基础扫盲:js作用域及其优先级,有示例代码。

在 JavaScript 中&#xff0c;作用域指的是变量和函数的可访问性和可见性。 JavaScript 中的作用域有以下几种&#xff1a; 1. 全局作用域&#xff08;Global Scope&#xff09;&#xff1a;全局作用域是指在代码中任何地方都可以访问的作用域。在全局作用域中声明的变量和函数…

DFS之连通性模型——AcWing 1112. 迷宫

DFS之连通性模型 定义 DFS&#xff08;深度优先搜索&#xff0c;Depth-First Search&#xff09;之连通性模型主要用于图论问题中判断图的连通性&#xff0c;即确定图中的所有节点是否可以通过边相互到达。 DFS&#xff08;深度优先搜索&#xff0c;Depth-First Search&…

深度学习——深度学习中感受野的计算

感受野 在卷积神经网络&#xff08;CNN&#xff09;中&#xff0c;感受野&#xff08;Receptive Field&#xff09; 是一个非常重要的概念。它描述了网络中某一层的输出&#xff08;通常是特征图上的一个像素点&#xff09;所对应的输入图像上的空间范围。这个范围代表了该输出…

Jelly Merge | Template + Editor(休闲益智游戏包)

Jelly Merge是Watermelon Games开发的一款完整游戏。 这款完全可定制的益智游戏具有简单但超级有趣的游戏玩法。 您下一次成功的完美起点&#xff01; 我们的优势 &#x1f9d1;&#x1f3fb;‍&#x1f4bb; 不和谐支持 &#x1f5c3;️ 详细文档 &#x1f6e0;️易于使用的工…

C# WPF 3D 数据孪生 系列六

数字孪生应用开发 应用开发中的布局需求 Grid基本使用 WPF 3D绘图 点云 系列五-CSDN博客 WPF UI 3D 多轴 机械臂 stl 模型UI交互-CSDN博客 WPF UI 3D 基本概念 点线三角面 相机对象 材质对象与贴图 3D地球 光源 变形处理 动作交互 辅助交互插件 系列三-CSDN博客 数字孪生 介…

【堆 优先队列】23. 合并 K 个升序链表

本文涉及知识点 堆 优先队列 LeetCode23. 合并 K 个升序链表 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 示例 1&#xff1a; 输入&#xff1a;lists [[1,4,5],[1,3,4],[2,6]] 输出&#…

本地Windows电脑 连接 Windows 服务器

Windows电脑 连接 Windows 服务器 方式1&#xff1a;直接搜索 在电脑的搜索栏&#xff0c;输入“远程桌面连接” 可以选择点击 “打开” 或者直接按 回车键 “Enter”&#xff0c;打开 远程桌面连接 方式2&#xff1a;运行框打开服务器连接 同时按&#xff1a;Windows徽标键…

【BUUCTF-PWN】10-bjdctf_2020_babystack

简单的栈溢出&#xff0c;ret2text 64位&#xff0c;开启了NX保护 执行效果&#xff1a; main函数&#xff1a; 因为读入的字符长度可以由用户输入的第一个参数值决定&#xff0c;因此read函数存在栈溢出 覆盖距离为0x108 存在后门函数&#xff1a; 后门函数地址0x4…

Vulnhub靶场DC-5练习

目录 0x00 准备0x01 主机信息收集0x02 站点信息收集0x03 漏洞查找与利用1. 利用burpsuite爆破文件包含的参数2. 文件包含3. nginx日志挂马4. 反弹shell5.漏洞利用和提权 0x04 总结 0x00 准备 下载链接&#xff1a;https://download.vulnhub.com/dc/DC-5.zip 介绍&#xff1a; …

(十三)MipMap

MipMap概念 滤波 采样 mipmap级别判定 问题&#xff1a;opengl如何判定应该使用下一级的mipmap呢&#xff1f; 通过glsl中的求偏导函数计算变化量决定 手动实现mipmap原理 1、生成mipmap的各个级别 2、修改vertexShader使得三角形随着时间变小 **** 需要更改Filter才能…

《昇思25天学习打卡营第8天|模型训练》

文章目录 今日所学&#xff1a;一、构建数据集二、定义神经网络模型三、了解超参、损失函数和优化器1. 超参2. 损失函数3. 优化器 四、训练与评估总结 今日所学&#xff1a; 在今天这一节我主要学习了模型的训练&#xff0c;知道了模型训练一般分为四个步骤&#xff1a; 构建…

[C++]——同步异步日志系统(2)

同步异步日志系统 一、 不定参函数1.1 不定参宏函数的使用1.2 C 语言中不定参函数的使用1.3 C不定参数使用 二、设计模式2.1 单列模式2.2 工厂模式2.3 建造者模式2.4 代理模式 在我们开发同步异步日志系统之前&#xff0c;需要了解一些相关的技术知识。 一、 不定参函数 在初学…

华为OD机试 - 考古学家 - 递归(Java 2024 D卷 200分)

华为OD机试 2024D卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;D卷C卷A卷B卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测…