C++复习核心精华

一、内存管理与智能指针

内存管理是C++区别于其他高级语言的关键特性,掌握好它就掌握了C++的灵魂。

1. 原始指针与内存泄漏

先来看看传统C++的内存管理方式:

void oldWay() {int* p = new int(42);  // 分配内存// 如果这里发生异常或提前return,下面的delete可能不会执行// 其他代码...delete p;  // 释放内存p = nullptr;  // 避免悬空指针}

这种方式有什么问题呢?太容易出错了!忘记delete、出现异常、提前返回...都会导致内存泄漏。就像你把菜做到一半,突然停电了,灶台上的火忘了关,后果很严重啊!

2. RAII原则与智能指针详解

C++引入了RAII原则(Resource Acquisition Is Initialization),资源的获取与初始化同时进行,资源的释放与对象销毁同时进行。这就像是给你的厨房装了个自动灭火器,不管发生什么,都能自动处理。

智能指针就是RAII的典型应用:

std::unique_ptr

独占所有权的智能指针,不能复制,但可以移动。

void betterWay() {std::unique_ptr<int> p1 = std::make_unique<int>(42);  // C++14引入// 或者在C++11中:std::unique_ptr<int> p1(new int(42));// 即使这里发生异常,p1也会自动释放内存// 独占所有权,不能复制// std::unique_ptr<int> p2 = p1;  // 编译错误!// 但可以转移所有权std::unique_ptr<int> p3 = std::move(p1);  // p1现在为空// 离开作用域时,p3自动释放内存}

unique_ptr的内部实现非常轻量,几乎没有性能开销,是最常用的智能指针。就像是你的私人助手,只为你一个人服务,效率极高。

std::shared_ptr与引用计数

共享所有权的智能指针,通过引用计数机制实现。

void sharedOwnership() {// 创建一个shared_ptrstd::shared_ptr<int> sp1 = std::make_shared<int>(100);std::cout << "引用计数: " << sp1.use_count() << std::endl;  // 输出:1{// 创建sp1的副本,共享所有权std::shared_ptr<int> sp2 = sp1;std::cout << "引用计数: " << sp1.use_count() << std::endl;  // 输出:2// 修改值,两个指针指向同一个对象*sp2 = 200;std::cout << "sp1指向的值: " << *sp1 << std::endl;  // 输出:200}  // sp2离开作用域,引用计数减1std::cout << "引用计数: " << sp1.use_count() << std::endl;  // 输出:1}  // sp1离开作用域,引用计数减为0,内存被释放

shared_ptr内部维护两块内存:一个是数据本身,一个是控制块(包含引用计数等信息)。这有点像合租房子,大家共同负责,最后一个离开的人负责关灯锁门。

std::weak_ptr与循环引用问题

weak_ptr不增加引用计数,用于解决循环引用问题。

class Node {public:std::string name;std::shared_ptr<Node> next;  // 指向下一个节点std::weak_ptr<Node> prev;    // 指向前一个节点(弱引用)Node(const std::string& n) : name(n) {}~Node() {std::cout << "销毁节点: " << name << std::endl;}};void circularReference() {auto node1 = std::make_shared<Node>("Node1");auto node2 = std::make_shared<Node>("Node2");// 创建循环引用node1->next = node2;node2->prev = node1;  // 弱引用不会增加引用计数// 使用弱引用if (auto temp = node2->prev.lock()) {  // 转换为shared_ptrstd::cout << "前一个节点是: " << temp->name << std::endl;}}  // 函数结束时,两个节点都能被正确释放

如果prev也使用shared_ptr,就会形成循环引用,导致内存泄漏。这就像两个人互相等对方先离开,结果谁也走不了。

3. 自定义删除器

有时我们需要在释放资源时执行特定操作,可以使用自定义删除器:

// 文件资源管理void customDeleter() {// 自定义删除器,确保文件正确关闭auto fileCloser = [](FILE* fp) {if (fp) {std::cout << "关闭文件" << std::endl;fclose(fp);}};// 使用自定义删除器的智能指针std::unique_ptr<FILE, decltype(fileCloser)> filePtr(fopen("data.txt", "r"), fileCloser);if (filePtr) {// 使用文件...char buffer[100];fread(buffer, 1, sizeof(buffer), filePtr.get());}// 离开作用域时,fileCloser会被调用}

这个例子完美展示了RAII的威力,无论函数如何退出,文件都会被正确关闭。就像雇了专业保洁员,走的时候一定会把屋子打扫干净。

二、模板编程的艺术

模板是C++最强大的特性之一,让你写出既通用又高效的代码。它不仅仅是代码复用工具,更是元编程的基础。

1. 函数模板深入理解

基本函数模板我们都了解,但你知道模板还能做这些事吗?

// 可变参数模板template<typename T>T sum(T value) {return value;}template<typename T, typename... Args>T sum(T first, Args... args) {return first + sum(args...);}// 使用int total = sum(1, 2, 3, 4, 5);  // 返回15std::string s = sum(std::string("Hello"), " ", "World");  // 返回"Hello World"

递归模板展开是一个非常强大的技术,这段代码的展开过程就像俄罗斯套娃,层层展开,最终计算出结果。

2. 类模板特化

模板特化允许我们为特定类型提供特殊实现:

// 主模板template<typename T>class DataHandler {public:void process(T data) {std::cout << "处理通用数据: " << data << std::endl;}};// 针对std::string的完全特化template<>class DataHandler<std::string> {public:void process(std::string data) {std::cout << "处理字符串: " << data << std::endl;// 字符串特有的处理逻辑...}};// 部分特化(针对指针类型)template<typename T>class DataHandler<T*> {public:void process(T* data) {if (data) {std::cout << "处理指针指向的数据: " << *data << std::endl;} else {std::cout << "空指针!" << std::endl;}}};

模板特化就像餐厅里的"定制菜单",根据不同的"食客"(类型)提供量身定制的"服务"(实现)。

3. SFINAE与类型萃取

SFINAE (Substitution Failure Is Not An Error) 是模板元编程的重要技术,允许编译器在模板实例化失败时继续尝试其他重载。

// 检查类型是否有size()成员函数template<typename T>struct has_size {private:template<typename C> static constexpr auto test(int) -> decltype(std::declval<C>().size(), bool()) { return true; }template<typename C> static constexpr bool test(...) { return false; }public:static constexpr bool value = test<T>(0);};// 根据类型特性选择不同实现template<typename Container>typename std::enable_if<has_size<Container>::value, void>::typeprintSize(const Container& c) {std::cout << "容器大小: " << c.size() << std::endl;}template<typename T>typename std::enable_if<!has_size<T>::value, void>::typeprintSize(const T&) {std::cout << "此类型没有size()方法" << std::endl;}

在C++17中,我们可以使用if constexpr简化这种代码:

template<typename Container>void printSize(const Container& c) {if constexpr (has_size<Container>::value) {std::cout << "容器大小: " << c.size() << std::endl;} else {std::cout << "此类型没有size()方法" << std::endl;}}

这种技术就像是编译时的"魔法侦探",能够根据类型的特性自动选择最合适的实现路径。

三、STL深度剖析

STL是C++标准库的核心部分,掌握它可以避免重复造轮子,大幅提高开发效率。

1. 容器性能对比与选择指南

不同容器有不同的性能特点,选择合适的容器至关重要:

容器随机访问插入 / 删除 (中间)插入 / 删除 (首 / 尾)查找特点
vectorO(1)O(n)O(1) 尾部O(n)连续内存,缓存友好
listO(n)O(1)O(1)O(n)双向链表,稳定迭代器
dequeO(1)O(n)O(1) 首尾O(n)分段连续内存
set/mapO(log n)O(log n)O(log n)O(log n)红黑树实现,有序
unordered_set/mapO(1) 平均O(1) 平均O(1) 平均O(1) 平均哈希表实现,无序
// 性能敏感场景选择指南void containerChoice() {// 1. 频繁随机访问,较少插入删除 -> vectorstd::vector<int> v;// 2. 频繁在两端操作 -> dequestd::deque<int> d;// 3. 频繁在中间插入删除 -> liststd::list<int> l;// 4. 需要有序并快速查找 -> set/mapstd::map<std::string, int> m;// 5. 需要最快的查找,不要求有序 -> unordered_set/mapstd::unordered_map<std::string, int> um;}

选择合适的容器就像选择合适的工具,木匠不会用锤子切木头,也不会用锯子钉钉子。

2. 算法与迭代器配合使用

STL的强大在于算法与容器的解耦,通过迭代器连接:

void algorithmDemo() {std::vector<int> numbers = {1, 5, 3, 4, 2};// 查找auto it = std::find(numbers.begin(), numbers.end(), 3);if (it != numbers.end()) {std::cout << "找到: " << *it << " 位置: " << std::distance(numbers.begin(), it) << std::endl;}// 排序std::sort(numbers.begin(), numbers.end());// 二分查找(要求已排序)bool exists = std::binary_search(numbers.begin(), numbers.end(), 3);// 变换std::vector<int> squared;std::transform(numbers.begin(), numbers.end(), std::back_inserter(squared),[](int x) { return x * x; });// 累加int sum = std::accumulate(numbers.begin(), numbers.end(), 0);// 自定义排序std::sort(numbers.begin(), numbers.end(), [](int a, int b) {return std::abs(a) < std::abs(b);  // 按绝对值排序});}

3. 自定义容器的迭代器

理解迭代器设计可以帮助我们更好地使用STL,甚至为自定义容器实现迭代器:

// 简单的环形缓冲区template<typename T, size_t Size>class CircularBuffer {private:T data_[Size];size_t head_ = 0;size_t tail_ = 0;size_t size_ = 0;public:// 迭代器实现class iterator {private:CircularBuffer<T, Size>* buffer_;size_t index_;size_t count_;public:// 迭代器类型定义(满足STL要求)using iterator_category = std::forward_iterator_tag;using value_type = T;using difference_type = std::ptrdiff_t;using pointer = T*;using reference = T&;iterator(CircularBuffer<T, Size>* buffer, size_t index, size_t count): buffer_(buffer), index_(index), count_(count) {}// 迭代器操作T& operator*() { return buffer_->data_[index_]; }iterator& operator++() {index_ = (index_ + 1) % Size;++count_;return *this;}bool operator!=(const iterator& other) const {return count_ != other.count_;}};// 容器方法void push(const T& value) {data_[tail_] = value;tail_ = (tail_ + 1) % Size;if (size_ < Size) {++size_;} else {head_ = (head_ + 1) % Size;  // 覆盖最老的元素}}// 提供迭代器iterator begin() { return iterator(this, head_, 0); }iterator end() { return iterator(this, head_, size_); }};// 使用示例void customContainerDemo() {CircularBuffer<int, 5> buffer;for (int i = 0; i < 7; ++i) {buffer.push(i);}// 此时缓冲区包含:2, 3, 4, 5, 6// 使用for-each循环(需要begin/end支持)for (const auto& value : buffer) {std::cout << value << " ";  // 输出:2 3 4 5 6}// 也可以与STL算法配合使用auto sum = std::accumulate(buffer.begin(), buffer.end(), 0);std::cout << "\n总和: " << sum << std::endl;  // 输出:20}

自定义迭代器需要满足特定的接口要求,这样才能与STL算法无缝配合。就像设计插头和插座,只要遵循标准,任何设备都能正常工作。

四、现代C++特性(C++11/14/17/20)

现代C++引入了大量新特性,极大提升了开发效率和代码质量。

1. 移动语义与右值引用

移动语义允许我们在不需要深拷贝的情况下转移资源所有权,大幅提升性能:

class BigData {private:int* data_;size_t size_;public:// 构造函数BigData(size_t size) : size_(size) {data_ = new int[size];std::cout << "分配 " << size << " 个整数" << std::endl;}// 析构函数~BigData() {delete[] data_;std::cout << "释放内存" << std::endl;}// 拷贝构造函数(深拷贝)BigData(const BigData& other) : size_(other.size_) {data_ = new int[size_];std::memcpy(data_, other.data_, size_ * sizeof(int));std::cout << "拷贝 " << size_ << " 个整数(昂贵操作)" << std::endl;}// 移动构造函数BigData(BigData&& other) noexcept : data_(other.data_), size_(other.size_) {other.data_ = nullptr;  // 防止源对象释放内存other.size_ = 0;std::cout << "移动资源(快速操作)" << std::endl;}// 移动赋值运算符BigData& operator=(BigData&& other) noexcept {if (this != &other) {delete[] data_;  // 释放当前资源// 窃取资源data_ = other.data_;size_ = other.size_;// 将源对象置于有效但可析构状态other.data_ = nullptr;other.size_ = 0;std::cout << "移动赋值(快速操作)" << std::endl;}return *this;}};// 演示移动语义优势void moveSemantics() {std::vector<BigData> v;std::cout << "创建临时对象并添加到vector:" << std::endl;v.push_back(BigData(1000000));  // 使用移动构造函数,避免深拷贝std::cout << "\n创建命名对象:" << std::endl;BigData d1(1000000);std::cout << "\n复制添加到vector:" << std::endl;v.push_back(d1);  // 使用拷贝构造函数,进行深拷贝std::cout << "\n移动添加到vector:" << std::endl;v.push_back(std::move(d1));  // 显式使用移动语义// 注意:此时d1已被移动,处于有效但未指定状态,不应再使用它的值}

移动语义就像是把一整本书直接交给别人,而不是复印一份再给他。在处理大型资源时,这种差异极其显著。

2. 完美转发与通用引用

完美转发允许函数模板精确地传递参数,保持其值类别(左值/右值):

// 工厂函数示例template<typename T, typename... Args>std::unique_ptr<T> make_unique(Args&&... args) {return std::unique_ptr<T>(new T(std::forward<Args>(args)...));}// 完美转发包装器template<typename Func, typename... Args>auto forwardingWrapper(Func&& func, Args&&... args) -> decltype(func(std::forward<Args>(args)...)) {std::cout << "转发参数到函数" << std::endl;return func(std::forward<Args>(args)...);}void perfectForwarding() {auto print = [](const std::string& s) { std::cout << "左值: " << s << std::endl; return s.length();};auto printRValue = [](std::string&& s) { std::cout << "右值: " << s << std::endl; return s.length();};std::string str = "Hello";// 转发左值forwardingWrapper(print, str);// 转发右值forwardingWrapper(printRValue, std::move(str));// 创建对象并完美转发参数auto p = make_unique<std::vector<int>>(5, 10);  // 创建包含5个10的vector}

完美转发就像是一个完美的中间人,既不添加任何东西,也不减少任何东西,原封不动地传递参数。

3. Lambda表达式与捕获技巧

Lambda表达式让函数式编程在C++中变得简单优雅:

void lambdaExamples() {int x = 10;// 基本lambdaauto add = [](int a, int b) { return a + b; };std::cout << "5 + 3 = " << add(5, 3) << std::endl;// 捕获变量auto addX = [x](int a) { return a + x; };std::cout << "5 + x = " << addX(5) << std::endl;// 引用捕获(可修改外部变量)auto incrementX = [&x]() { x++; };incrementX();std::cout << "x现在是: " << x << std::endl;  // 输出:11// 混合捕获int y = 20;auto calculate = [x, &y](int a) { y += a;  // 修改yreturn x * y;  // 使用x的副本};std::cout << "计算结果: " << calculate(5) << std::endl;std::cout << "y现在是: " << y << std::endl;  // y被修改// 捕获this指针struct Counter {int value = 0;auto increment() {// 捕获this指针,可访问成员变量return [this]() { ++value; };}void print() {std::cout << "计数: " << value << std::endl;}};Counter c;auto inc = c.increment();inc();inc();c.print();  // 输出:计数: 2// 初始化捕获(C++14)auto sum = [sum = 0](int value) mutable {sum += value;return sum;};std::cout << sum(1) << std::endl;  // 1std::cout << sum(2) << std::endl;  // 3std::cout << sum(3) << std::endl;  // 6}

Lambda表达式就像是随手写下的小纸条,简洁而直接,让代码更加紧凑易读。

4. constexpr与编译期计算

constexpr允许在编译期执行计算,提高运行时性能:

// 编译期计算斐波那契数列constexpr int fibonacci(int n) {return (n <= 1) ? n : fibonacci(n-1) + fibonacci(n-2);}// 编译期计算阶乘constexpr int factorial(int n) {return (n <= 1) ? 1 : n * factorial(n-1);}void constexprDemo() {// 编译期计算constexpr int fib10 = fibonacci(10);constexpr int fact5 = factorial(5);std::cout << "斐波那契(10) = " << fib10 << std::endl;std::cout << "阶乘(5) = " << fact5 << std::endl;// 编译期数组大小constexpr int size = factorial(5);int arr[size];  // 使用编译期常量作为数组大小// C++17: constexpr iftemplate<typename T>auto getValue(T t) {if constexpr (std::is_pointer_v<T>) {return *t;  // 指针类型} else {return t;   // 非指针类型}}int x = 42;int* p = &x;std::cout << getValue(x) << std::endl;  // 42std::cout << getValue(p) << std::endl;  // 42}

constexpr就像是提前做好的作业,编译器在编译时就把结果算出来了,运行时直接使用结果,不需要再计算。

总结

记住,C++的威力不仅在于它的特性,更在于如何巧妙地组合这些特性,解决实际问题。就像武术高手,招式并不是最重要的,关键是如何融会贯通,形成自己的"功夫"。

希望这些深度解析能帮助你更好地理解C++的精髓。有什么不明白的地方,或者想要了解的其他C++话题,随时告诉我!我们一起在C++的海洋中探索更多奥秘!

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

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

相关文章

期货反向跟单软件—提高盘手杠杆的方式及剖析

在期货反向跟单领域&#xff0c;期货跟单软件对盘手杠杆的调节&#xff0c;是整个策略运作的核心环节之一。其背后蕴含着科学的金融逻辑。​ 期货跟单软件提高盘手杠杆主要通过两种方式。第一种是降低期货保证金。在盘手资金总量固定的情况下&#xff0c;保证金降低&#xff0…

【计算机网络】基于UDP进行socket编程——实现服务端与客户端业务

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;Linux &#x1f339;往期回顾&#x1f339;&#xff1a; 【Linux笔记】——网络基础 &#x1f516;流水不争&#xff0c;争的是滔滔不息 一、UDPsocket编程UDPsocket编…

ae卡通打架烟雾特效

1、创建一个合成&#xff08;合成1&#xff09;&#xff0c;右键创建形状图层&#xff0c;使用椭圆工具&#xff0c;长按shift键拖动鼠标左键画出圆形&#xff0c;同时按ctrlalthome三个键使圆形中心锚点对齐圆心&#xff0c;关闭描边&#xff0c;圆形图层填充白色。 2、选择形…

UE5 Va Res发送请求、处理请求、json使用

文章目录 介绍发送一个Get请求发送Post请求设置请求头请求体带添json发送请求完整的发送蓝图 处理收到的数据常用的json处理节点 介绍 UE5 自带的Http插件&#xff0c;插件内自带json解析功能 发送一个Get请求 只能写在事件图表里 发送Post请求 只能写在事件图表里 设置…

SQL 结构化模型设计与现代技术融合深度解读

摘要 本文系统展示了基于 JSON Schema 的 SQL 结构化模型设计&#xff0c;包括通用定义、四大基本操作&#xff08;SELECT、INSERT、UPDATE、DELETE&#xff09;的模型规范&#xff0c;以及面向现代场景的设计扩展。重点结合数据权限控制、乐观锁并发控制、表单自动化、自定义…

el-dialog 组件 多层嵌套 被遮罩问题

<el-dialog title"提示" :visible.sync"dialogBindUserVisible" width"30%" append-to-body :before-close"handleClose"> <span>这是一段信息</span> <span slot"footer" class"dialog-footer&q…

【KWDB 2025 创作者计划】_KWDB时序数据库特性及跨模查询

一、概述 数据库的类型多种多样&#xff0c;关系型数据库、时序型数据库、非关系型数据库、内存数据库、分布式数据库、图数据库等等&#xff0c;每种类型都有其特定的使用场景和优势&#xff0c;KaiwuDB 是一款面向 AIoT 场景的分布式、多模融合、支持原生 AI 的数据库…

学习心得(12-13)HTML 是什么 abort函数and自定义异常

一. abort函数 将后端的数据给到前端 二. 自定义异常 要结合abort函数使用 1.编写的时候都在abort的函数这个文件里面 错误信息在前端页面的展示&#xff1a; 如果想要在出现异常的时候返回一个页面&#xff1a; 1. 新建一个HTML文件 例如命名为404 2.将图库里的图片拖入…

理解全景图像拼接

1 3D到2D透视投影 三维空间上点 p 投影到二维空间 q 有两种方式&#xff1a;1&#xff09;正交投影&#xff0c;2&#xff09;透视投影。 正交投影直接舍去 z 轴信息&#xff0c;该模型仅在远心镜头上是合理的&#xff0c;或者对于物体深度远小于其到摄像机距离时的近似模型。…

Linux基本指令篇 —— whoami指令

whoami 是 Linux 和 Unix 系统中一个简单但实用的命令&#xff0c;全称 Who Am I&#xff08;我是谁&#xff09;。它的功能是显示当前登录用户的用户名。以下是关于 whoami 的详细解析&#xff1a; 目录 1. 基本用法 2. 命令特点 3. 实际应用场景 场景 1&#xff1a;脚本中…

华为OD机试真题——仿LISP运算(2025B卷:200分)Java/python/JavaScript/C/C++/GO最佳实现

2025 B卷 200分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…

创建dummy

访客_dc1fc4 class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int left0;int right0;int n nums.size(); int sum0;int ans100001; for(right0;right<n;right) { sumnums[right]; //每次更新右端点之后&#xff0c;立即向右移动…

面向恶劣条件的道路交通目标检测----大创自用(当然你也可以在里面学到很多东西)

全部内容梳理 目标检测的两个任务&#xff1a; 预测标签 边界框 语义分割 实力分割 一个是类别 一个是实例级别 分类任务把每个图像当作一张图片看待 所有解决方法是先生成候选区域 再进行分类 置信度&#xff1a; 包括对类别和边界框预测的自信程度 输出分类和IOU分数的…

需求管理工具使用不当,如何优化?

要优化需求管理工具的使用&#xff0c;需从选择合适工具、规范使用流程、加强用户培训、统一数据结构、定期审查与优化使用配置五个方面着手。其中&#xff0c;选择合适工具是前提。错误的工具选择往往会导致项目沟通效率低、需求追踪失效甚至造成交付物偏离客户预期。因此&…

openwrt虚拟机安装调试

分类 lienol lean immortalwrt 一、获取固件 &#xff08;1&#xff09;下载地址 1.官网构建下载 OpenWrt Firmware Selector 官网 OpenWrt Firmware Selector 2.第三方构建网站 ImmortalWrt Firmware Selector ImmortalWrt Firmware Selector 3.第三方构建下载 ht…

Apache OFBiz 17.12.01 的远程命令执行漏洞 -Java 反序列化 + XML-RPC 请求机制

目录 漏洞原理 &#x1f3af; 一、漏洞背景&#xff08;CVE-2020-9496&#xff09; ⚙️ 二、攻击原理简述 &#x1f9f1; 三、完整攻击流程步骤详解 &#x1f50e; 1. 信息收集 &#x1f6e0;️ 2. 工具准备 &#x1f9ea; 3. 构造初始 payload&#xff1a;下载恶意脚本…

最好用的wordpress外贸主题

产品展示独立站wordpress主题 橙色的首页大banner外贸英文wordpress主题&#xff0c;适合用于产品展示型的外贸网站。 https://www.jianzhanpress.com/?p8556 Machine机器wordpress模板 宽屏简洁实用的wordpress外贸建站模板&#xff0c;适合工业机器生产、加工、制造的外贸…

Q1:Go协程、Channel通道 被close后,读会带来什么问题?

在 Go 语言中&#xff0c;Channel&#xff08;通道&#xff09;关闭后读取的行为是一个常见但需要谨慎处理的问题。以下是详细的分析和注意事项&#xff1a; 1. 关闭 Channel 后读取的行为 (1) 读取已关闭的 Channel 剩余数据仍可读取&#xff1a; 关闭 Channel 后&#xff0…

【AI Study】第三天,Python基础 - 同NumPy类似的类库

学习计划&#xff1a;AI Study&#xff0c;学习计划源码地址&#xff1a;https://github.com/co-n00b/AI-Study.git 2025-05-23 在学习NumPy的过程中&#xff0c;除了了解NumPy之外&#xff0c;我们也对比看看其他类似的类库都有什么&#xff1f;各自的优缺点是什么&#xff1…

基于aspnet,微信小程序,mysql数据库,在线微信小程序汽车故障预约系统

详细视频:【基于aspnet,微信小程序,mysql数据库,在线微信小程序汽车故障预约系统。-哔哩哔哩】 https://b23.tv/zfqLWPV