C++基础:(十六)priority_queue和deque的深度解析

目录

前言

[一、priority_queue 深度解析](#一、priority_queue 深度解析)

[1.1 priority_queue 的概念与特性](#1.1 priority_queue 的概念与特性)

[1.2 priority_queue 的核心接口与使用示例](#1.2 priority_queue 的核心接口与使用示例)

[1.2.1 基本使用示例(大堆与小堆)](#1.2.1 基本使用示例(大堆与小堆))

[1.2.2 存储自定义类型的 priority_queue](#1.2.2 存储自定义类型的 priority_queue)

[1.2.3 注意事项](#1.2.3 注意事项)

[1.3 priority_queue 的模拟实现](#1.3 priority_queue 的模拟实现)

[1.3.1 模拟实现代码](#1.3.1 模拟实现代码)

[1.4 priority_queue 的经典实战场景](#1.4 priority_queue 的经典实战场景)

[1.4.1 数组中第 K 个最大的元素(LeetCode 215)](#1.4.1 数组中第 K 个最大的元素(LeetCode 215))

[1.4.2 合并 K 个升序链表(LeetCode 23)](#1.4.2 合并 K 个升序链表(LeetCode 23))

二、底层容器深度解析:deque

[2.1 deque 的概念与特性](#2.1 deque 的概念与特性)

[2.2 deque 的底层结构](#2.2 deque 的底层结构)

[2.3 deque 的迭代器实现](#2.3 deque 的迭代器实现)

[2.4 deque 的优缺点](#2.4 deque 的优缺点)

[2.4.1 优点](#2.4.1 优点)

[2.4.2 缺点](#2.4.2 缺点)

[2.5 为何选择 deque 作为 stack 和 queue 的默认底层容器](#2.5 为何选择 deque 作为 stack 和 queue 的默认底层容器)

三、容器适配器总结与最佳实践

[3.1 三种容器适配器的核心对比](#3.1 三种容器适配器的核心对比)

[3.2 底层容器选择建议](#3.2 底层容器选择建议)

[3.3 最佳实践](#3.3 最佳实践)

[3.4 常见误区](#3.4 常见误区)

总结


前言

在C++标准库中,容器是构建高效算法的基石,而priority_queuedeque作为两种特性迥异却同样强大的数据结构,分别代表了优先级管理与线性操作的典范。 priority_queue基于堆实现,以O(log n)的代价维护元素优先级,成为任务调度、贪心算法等场景的核心工具;而deque通过分块数组的巧妙设计,实现了首尾O(1)时间复杂度的插入删除,在滑动窗口、缓存系统等领域展现出不可替代性。本文将从底层实现、性能对比到典型应用场景,深入剖析二者的设计理念与工程实践价值,帮助大家在面对不同需求时做出精准选择。现在就让我们正式开始吧!


一、priority_queue 深度解析

1.1 priority_queue 的概念与特性

priority_queue(优先队列)是一种特殊的队列,其核心特性是 "队头元素始终是队列中优先级最高的元素"(默认是最大值优先)。

priority_queue 的底层实现是 "堆"(heap),堆是一种完全二叉树,分为大堆(根节点是最大值)和小堆(根节点是最小值)。STL 中的 priority_queue 默认是大堆。

priority_queue 的核心操作:

  • 插入元素(push):将元素插入堆中,并调整堆结构,维持堆的特性;
  • 删除元素(pop):删除堆顶元素(优先级最高的元素),并调整堆结构;
  • 访问堆顶元素(top):获取优先级最高的元素。

priority_queue 适用于需要 "动态获取优先级最高元素" 的场景,例如任务调度(优先执行高优先级任务)、找出数组中第 K 大的元素等。

1.2 priority_queue 的核心接口与使用示例

STL 为 priority_queue 提供的常用成员函数如下表所示:

函数声明 接口说明
priority_queue() 构造一个空的优先队列(默认大堆)
priority_queue(InputIterator first, InputIterator last) 用 [first, last) 区间的元素构造优先队列
empty() 检测优先队列是否为空,为空返回 true,否则返回 false
size() 返回优先队列中有效元素的个数
top() 返回堆顶元素的引用(非 const 对象调用)
const T& top() const 返回堆顶元素的 const 引用(const 对象调用)
push(const T& x) 将元素 x 插入优先队列,并调整堆结构
pop() 删除堆顶元素,并调整堆结构(不返回元素值)

1.2.1 基本使用示例(大堆与小堆)

cpp 复制代码
#include <iostream>
#include <queue>
#include <vector>
#include <functional>  // greater<> 仿函数的头文件
using namespace std;

int main() {
    // 1. 默认大堆(底层用vector,比较方式为less<>)
    priority_queue<int> maxHeap;
    maxHeap.push(3);
    maxHeap.push(1);
    maxHeap.push(4);
    maxHeap.push(2);
    
    cout << "大堆的堆顶元素:" << maxHeap.top() << endl;  // 输出:4
    cout << "大堆的大小:" << maxHeap.size() << endl;      // 输出:4
    
    maxHeap.pop();
    cout << "删除堆顶后,大堆的堆顶元素:" << maxHeap.top() << endl;  // 输出:3
    
    // 2. 小堆(需指定比较方式为greater<>)
    // 模板参数:元素类型、底层容器类型、比较仿函数
    priority_queue<int, vector<int>, greater<int>> minHeap;
    minHeap.push(3);
    minHeap.push(1);
    minHeap.push(4);
    minHeap.push(2);
    
    cout << "小堆的堆顶元素:" << minHeap.top() << endl;  // 输出:1
    minHeap.pop();
    cout << "删除堆顶后,小堆的堆顶元素:" << minHeap.top() << endl;  // 输出:2
    
    // 3. 用区间构造优先队列
    vector<int> nums = {5, 2, 7, 3, 9};
    priority_queue<int> heap(nums.begin(), nums.end());
    cout << "区间构造的大堆堆顶:" << heap.top() << endl;  // 输出:9
    
    return 0;
}

1.2.2 存储自定义类型的 priority_queue

当 priority_queue 存储自定义类型时,需要为该类型提供比较规则(重载<>运算符,或自定义比较仿函数),因为 priority_queue 需要通过比较来维护堆结构。

示例:存储日期类型的 priority_queue

cpp 复制代码
#include <iostream>
#include <queue>
#include <vector>
#include <functional>
using namespace std;

// 日期类
class Date {
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}
    
    // 重载<运算符:用于大堆(默认比较方式)
    bool operator<(const Date& d) const {
        // 按年、月、日的顺序比较,年份大的日期优先级高
        if (_year != d._year) {
            return _year < d._year;
        }
        if (_month != d._month) {
            return _month < d._month;
        }
        return _day < d._day;
    }
    
    // 重载>运算符:用于小堆
    bool operator>(const Date& d) const {
        if (_year != d._year) {
            return _year > d._year;
        }
        if (_month != d._month) {
            return _month > d._month;
        }
        return _day > d._day;
    }
    
    // 友元函数:重载<<运算符,用于输出Date对象
    friend ostream& operator<<(ostream& os, const Date& d) {
        os << d._year << "-" << d._month << "-" << d._day;
        return os;
    }
    
private:
    int _year;
    int _month;
    int _day;
};

int main() {
    // 大堆:优先级高的日期(年份大、月份大、日期大)在堆顶
    priority_queue<Date> maxHeap;
    maxHeap.push(Date(2018, 10, 29));
    maxHeap.push(Date(2018, 10, 28));
    maxHeap.push(Date(2019, 1, 1));
    
    cout << "大堆堆顶日期:" << maxHeap.top() << endl;  // 输出:2019-1-1
    maxHeap.pop();
    cout << "删除堆顶后,大堆堆顶日期:" << maxHeap.top() << endl;  // 输出:2018-10-29
    
    // 小堆:优先级低的日期(年份小、月份小、日期小)在堆顶
    priority_queue<Date, vector<Date>, greater<Date>> minHeap;
    minHeap.push(Date(2018, 10, 29));
    minHeap.push(Date(2018, 10, 28));
    minHeap.push(Date(2019, 1, 1));
    
    cout << "小堆堆顶日期:" << minHeap.top() << endl;  // 输出:2018-10-28
    minHeap.pop();
    cout << "删除堆顶后,小堆堆顶日期:" << minHeap.top() << endl;  // 输出:2018-10-29
    
    return 0;
}

1.2.3 注意事项

  1. priority_queue 的**pop()**函数只删除堆顶元素,不返回该元素的值;
  2. 访问top()前,必须确保优先队列不为空,否则会导致未定义行为;
  3. 自定义类型必须提供比较规则:
    • 若使用默认大堆(priority_queue<T>),需重载<运算符;
    • 若使用小堆(priority_queue<T, vector<T>, greater<T>>),需重载>运算符;
  4. 底层容器默认是 vector,也可以指定 deque,但不支持 list(list 不支持随机访问迭代器,无法高效维护堆结构)。

1.3 priority_queue 的模拟实现

priority_queue 的底层是堆,因此模拟实现的核心是:

  1. 选择合适的底层容器(支持随机访问、尾插、尾删);
  2. 实现堆的核心操作:建堆(make_heap)、插入元素(push_heap)、删除元素(pop_heap)

堆的核心算法(以大堆为例):

  • **向上调整(push_heap):**插入元素到堆尾后,将该元素向上调整,维持大堆特性;
  • **向下调整(pop_heap):**删除堆顶元素后,将堆尾元素移到堆顶,再向下调整,维持大堆特性;
  • **建堆(make_heap):**将一个普通数组转换为堆,从最后一个非叶子节点开始向下调整。

1.3.1 模拟实现代码

cpp 复制代码
#include <vector>
#include <cassert>
#include <functional>  // 用于greater<>仿函数
namespace bite {
    // 模板参数:T-元素类型,Container-底层容器类型,Compare-比较仿函数(默认大堆)
    template <class T, class Container = std::vector<T>, class Compare = std::less<T>>
    class priority_queue {
    public:
        // 构造函数:默认构造
        priority_queue() {}
        
        // 区间构造:用[first, last)区间的元素构造堆
        template <class InputIterator>
        priority_queue(InputIterator first, InputIterator last)
            : _container(first, last) {
            // 建堆:从最后一个非叶子节点开始向下调整
            int parent = (_container.size() - 2) / 2;
            for (int i = parent; i >= 0; --i) {
                adjustDown(i);
            }
        }
        
        // 检测优先队列是否为空
        bool empty() const {
            return _container.empty();
        }
        
        // 获取优先队列的大小
        size_t size() const {
            return _container.size();
        }
        
        // 访问堆顶元素
        T& top() {
            assert(!empty());
            return _container[0];
        }
        
        const T& top() const {
            assert(!empty());
            return _container[0];
        }
        
        // 插入元素:尾插后向上调整
        void push(const T& x) {
            _container.push_back(x);
            adjustUp(_container.size() - 1);
        }
        
        // 删除堆顶元素:交换堆顶与堆尾元素,尾删后向下调整
        void pop() {
            assert(!empty());
            // 交换堆顶和堆尾元素
            std::swap(_container[0], _container[_container.size() - 1]);
            // 尾删堆尾元素(原堆顶)
            _container.pop_back();
            // 向下调整堆顶元素
            adjustDown(0);
        }
        
    private:
        // 向上调整:用于插入元素后维持堆特性
        void adjustUp(size_t child) {
            Compare comp;  // 比较仿函数:大堆用less,小堆用greater
            size_t parent = (child - 1) / 2;  // 父节点索引
            
            while (child > 0) {
                // 若子节点优先级高于父节点(大堆:子>父;小堆:子<父),则交换
                if (comp(_container[parent], _container[child])) {
                    std::swap(_container[parent], _container[child]);
                    // 更新父、子节点索引,继续向上调整
                    child = parent;
                    parent = (child - 1) / 2;
                } else {
                    // 满足堆特性,调整结束
                    break;
                }
            }
        }
        
        // 向下调整:用于删除元素后维持堆特性
        void adjustDown(size_t parent) {
            Compare comp;
            size_t child = 2 * parent + 1;  // 左子节点索引
            
            while (child < _container.size()) {
                // 选择优先级更高的子节点(大堆:选较大的子节点;小堆:选较小的子节点)
                if (child + 1 < _container.size() && comp(_container[child], _container[child + 1])) {
                    child++;
                }
                
                // 若子节点优先级高于父节点,则交换
                if (comp(_container[parent], _container[child])) {
                    std::swap(_container[parent], _container[child]);
                    // 更新父、子节点索引,继续向下调整
                    parent = child;
                    child = 2 * parent + 1;
                } else {
                    break;
                }
            }
        }
        
    private:
        Container _container;  // 底层容器
    };
}

1.4 priority_queue 的经典实战场景

1.4.1 数组中第 K 个最大的元素(LeetCode 215)

题目描述:给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

解题思路

  • **方法一(大堆):**将所有元素入堆,然后弹出 k-1 个元素,剩余的堆顶元素即为第 k 个最大元素;
  • **方法二(小堆):**维护一个大小为 k 的小堆,遍历数组时,若元素大于堆顶,则替换堆顶并调整堆,遍历结束后堆顶即为第 k 个最大元素(空间复杂度更优)。

代码实现(大堆方法)

cpp 复制代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        // 构造大堆
        priority_queue<int> pq(nums.begin(), nums.end());
        
        // 弹出k-1个最大元素
        for (int i = 0; i < k - 1; ++i) {
            pq.pop();
        }
        
        // 剩余堆顶元素即为第k个最大元素
        return pq.top();
    }
};

// 测试代码
int main() {
    Solution sol;
    vector<int> nums = {3,2,1,5,6,4};
    int k = 2;
    cout << "第" << k << "个最大的元素:" << sol.findKthLargest(nums, k) << endl;  // 输出:5
    
    vector<int> nums2 = {3,2,3,1,2,4,5,5,6};
    k = 4;
    cout << "第" << k << "个最大的元素:" << sol.findKthLargest(nums2, k) << endl;  // 输出:4
    
    return 0;
}

1.4.2 合并 K 个升序链表(LeetCode 23)

题目描述:给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并成一个升序链表,返回合并后的链表。

解题思路

  • 核心需求:每次从 K 个链表的头节点中选择最小的节点,加入结果链表;
  • 用 **priority_queue(小堆)**存储每个链表的头节点,堆顶即为当前最小节点;
  • 每次取出堆顶节点,若该节点有后继节点,则将后继节点入堆;
  • 重复上述步骤,直到堆为空。

代码实现

cpp 复制代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;

// 链表节点定义
struct ListNode {
    int val;
    ListNode* next;
    ListNode(int x) : val(x), next(nullptr) {}
};

// 自定义比较仿函数:用于小堆(优先级队列存储ListNode*,按节点值从小到大排序)
struct Compare {
    bool operator()(ListNode* a, ListNode* b) {
        return a->val > b->val;
    }
};

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        // 小堆:存储每个链表的头节点
        priority_queue<ListNode*, vector<ListNode*>, Compare> pq;
        
        // 将所有非空链表的头节点入堆
        for (ListNode* list : lists) {
            if (list != nullptr) {
                pq.push(list);
            }
        }
        
        // 虚拟头节点,用于构建结果链表
        ListNode* dummy = new ListNode(0);
        ListNode* curr = dummy;
        
        while (!pq.empty()) {
            // 取出当前最小节点
            ListNode* minNode = pq.top();
            pq.pop();
            
            // 加入结果链表
            curr->next = minNode;
            curr = curr->next;
            
            // 若该节点有后继节点,入堆
            if (minNode->next != nullptr) {
                pq.push(minNode->next);
            }
        }
        
        return dummy->next;
    }
};

// 辅助函数:打印链表
void printList(ListNode* head) {
    while (head != nullptr) {
        cout << head->val << " ";
        head = head->next;
    }
    cout << endl;
}

// 测试代码
int main() {
    // 构建链表数组
    ListNode* l1 = new ListNode(1);
    l1->next = new ListNode(4);
    l1->next->next = new ListNode(5);
    
    ListNode* l2 = new ListNode(1);
    l2->next = new ListNode(3);
    l2->next->next = new ListNode(4);
    
    ListNode* l3 = new ListNode(2);
    l3->next = new ListNode(6);
    
    vector<ListNode*> lists = {l1, l2, l3};
    
    Solution sol;
    ListNode* mergedList = sol.mergeKLists(lists);
    printList(mergedList);  // 输出:1 1 2 3 4 4 5 6
    
    return 0;
}

二、底层容器深度解析:deque

在之前的博客中,我们提到 stack 和 queue 的默认底层容器是 deque,而 priority_queue 的默认底层容器是 vector。本节将深入解析 deque 的原理、特性及为何成为 stack 和 queue 的默认选择。

2.1 deque 的概念与特性

deque(双端队列,Double-Ended Queue) 是一种双开口的 "连续" 空间数据结构,支持在头尾两端进行插入和删除操作,且时间复杂度均为 O (1)。

deque 的核心特性:

  • 双端操作高效:头插(push_front)、头删(pop_front)、尾插(push_back)、尾删(pop_back)均为 O (1);
  • 空间利用率高:扩容时无需搬移大量元素(与 vector 相比);
  • 支持随机访问:可以通过下标访问元素([ ] 运算符),时间复杂度 O (1);
  • 并非真正连续:底层由一段段连续的小空间(缓冲区)拼接而成,通过中控器(map)管理这些缓冲区。

2.2 deque 的底层结构

deque 的底层结构较为复杂,主要由三部分组成:

  1. **缓冲区(buffer):**一段连续的小空间,用于存储实际数据,默认大小通常为 512 字节(可配置);
  2. 中控器(map):一个动态数组,存储各个缓冲区的地址指针,本质是一个指针数组;
  3. **迭代器(iterator):**deque 的迭代器是一个智能指针,包含四个核心成员:
  • cur:指向当前缓冲区中的当前元素;
  • first:指向当前缓冲区的起始位置;
  • last:指向当前缓冲区的末尾位置;
  • node:指向中控器中当前缓冲区对应的指针。

deque 的底层结构示意图:

当 deque 需要扩容时:

  • 若中控器还有剩余空间,直接分配新的缓冲区,并将其地址加入中控器;
  • 若中控器已满,则重新分配一个更大的中控器(通常是原大小的 2 倍),并将原中控器的指针拷贝到新中控器中。

2.3 deque 的迭代器实现

deque 的迭代器设计是其能够 "假装" 连续的关键。当迭代器移动到缓冲区的边界时,会自动切换到下一个或上一个缓冲区。

以**++it(迭代器自增)**为例,其核心逻辑:

  1. it->cur++:移动到当前缓冲区的下一个元素;
  2. it->cur == it->last(已到达当前缓冲区末尾):
    • it->node++:指向中控器的下一个缓冲区指针;
    • it->cur = it->node->first:指向新缓冲区的起始位置;
  3. 否则,保持当前缓冲区不变。

同理,**--it(迭代器自减)**会处理缓冲区的左边界切换。

2.4 deque 的优缺点

2.4.1 优点

  1. 双端操作高效:头尾插入 / 删除均为 O (1),优于 vector(头插 O (n))和 list(虽然也是 O (1),但空间开销大);
  2. 扩容效率高:扩容时无需搬移大量元素,只需分配新的缓冲区并更新中控器,优于 vector;
  3. 支持随机访问:可以通过下标快速访问元素,优于 list(不支持随机访问);
  4. 空间利用率高:相比 list,无需存储前驱 / 后继指针,空间开销更小。

2.4.2 缺点

  1. 遍历效率低:迭代器移动时需要频繁检测缓冲区边界,切换缓冲区的开销较大,遍历速度远低于 vector;
  2. 迭代器复杂:deque 的迭代器实现复杂,相比 vector 的普通指针迭代器,操作开销更大;
  3. 中间插入 / 删除效率低:在 deque 中间插入或删除元素时,需要搬移该位置前后的元素,时间复杂度 O (n),与 vector 相当,不如 list(O (1))。

2.5 为何选择 deque 作为 stack 和 queue 的默认底层容器

stack 和 queue 的核心操作有如下特点:

  • stack:仅需尾插、尾删、访问尾元素,无需遍历;
  • queue:仅需尾插、头删、访问头 / 尾元素,无需遍历。

deque 是完美契合 stack 和 queue 的需求的,同时避开了自身的缺点:

  1. 无需遍历:stack 和 queue 不需要迭代器遍历,因此 deque 遍历效率低的缺点不影响;
  2. 双端操作高效:stack 的尾插 / 尾删、queue 的尾插 / 头删均为 O (1),符合高效需求;
  3. 扩容效率高:相比 vector,deque 扩容时无需搬移大量元素,stack 和 queue 的元素增长效率更高;
  4. 内存利用率高:相比 list,deque 的空间开销更小,内存利用率更高。

因此,STL 选择 deque 作为 stack 和 queue 的默认底层容器,是综合考虑操作效率、空间开销后的最优选择。

三、容器适配器总结与最佳实践

3.1 三种容器适配器的核心对比

容器适配器 核心特性 底层容器 时间复杂度(关键操作) 适用场景
stack 后进先出(LIFO) deque(默认)、vector、list push/pop/top:O(1) 函数调用栈、表达式求值、括号匹配、回溯算法
queue 先进先出(FIFO) deque(默认)、list push/pop/front/back:O(1) 任务调度、消息队列、广度优先搜索(BFS)
priority_queue 优先级最高元素优先 vector(默认)、deque push:O(log n)、pop:O(log n)、top:O(1) 任务调度(高优先级优先)、Top K 问题、堆排序

3.2 底层容器选择建议

  1. stack 的底层容器选择:

    • 默认 deque:综合性能最优;
    • vector:适合元素数量稳定、无需频繁扩容的场景,随机访问效率略高于 deque;
    • list:适合元素频繁插入 / 删除且数量不确定的场景,但空间开销大。
  2. queue 的底层容器选择:

    • 默认 deque:综合性能最优;
    • list:适合元素频繁插入 / 删除的场景,但空间开销大;
    • 避免使用 vector:头删操作效率极低。
  3. priority_queue 的底层容器选择:

    • 默认 vector:随机访问效率高,堆调整算法(向上 / 向下调整)效率最优;
    • deque:支持随机访问,可作为备选,但堆调整效率略低于 vector;
    • 避免使用 list:不支持随机访问,无法高效维护堆结构。

3.3 最佳实践

  1. 优先使用 STL 自带的容器适配器,而非自定义实现:STL 的实现经过高度优化,效率更高、稳定性更好;
  2. 根据业务场景选择合适的容器适配器:
    • 需要 **"后进先出"**时,选择 stack;
    • 需要 **"先进先出"**时,选择 queue;
    • 需要**"优先级排序"** 时,选择 priority_queue;
  3. 存储自定义类型时,务必提供正确的比较规则:priority_queue 需重载<>运算符,stack 和 queue 无需(除非需要自定义底层容器);
  4. 避免在 stack、queue、priority_queue 中存储大量大型对象的拷贝:建议存储指针或智能指针,减少拷贝开销;
  5. 访问 **top ()、front ()、back ()**前,务必检测容器是否为空,避免未定义行为。

3.4 常见误区

  1. 认为 stack、queue、priority_queue 是容器:实际上它们是容器适配器,底层依赖其他容器实现;
  2. 认为 deque 是真正连续的空间:deque 是分段连续的,通过中控器和迭代器模拟连续空间;
  3. 滥用 priority_queue:priority_queue 的插入 / 删除操作是 O (log n),若无需优先级排序,应选择 queue 或 stack 以获得更高效率;
  4. 忽视自定义类型的比较规则:priority_queue 存储自定义类型时,若未提供正确的比较规则,会导致编译错误或逻辑错误。

总结

stack、queue 和 priority_queue 是 C++ STL 中非常实用的容器适配器,它们基于底层容器封装,提供了简洁高效的接口,适用于不同的业务场景。本文从概念、接口、模拟实现、实战应用到底层原理,全方位解析了这三种容器适配器,希望能帮助大家深入理解其设计思想与使用方法。

在实际开发中,应根据具体需求选择合适的容器适配器及底层容器,充分发挥 STL 的高效性和便捷性。同时,深入理解底层原理有助于解决复杂问题,优化代码性能。

如果本文对你有帮助,欢迎点赞、收藏、转发,也欢迎在评论区交流讨论!

相关推荐
C++ 老炮儿的技术栈3 小时前
include″″与includ<>的区别
c语言·开发语言·c++·算法·visual studio
Vallelonga3 小时前
Rust 设计模式 Marker Trait + Blanket Implementation
开发语言·设计模式·rust
BS_Li3 小时前
C++IO库
c++·io流
CHANG_THE_WORLD3 小时前
PDFium导出pdf 图像
开发语言·c++·pdf
Larry_Yanan3 小时前
QML学习笔记(四十三)QML与C++交互:上下文属性暴露
c++·笔记·qt·学习·ui·交互
owCode3 小时前
4-C++智能指针
开发语言·c++
liu****3 小时前
10.queue的模拟实现
开发语言·数据结构·c++·算法
宋恩淇要努力3 小时前
C++多态
c++
哦你看看4 小时前
学习Python 03
开发语言·windows·python