用c++写一个简单vector 设计思路使用::operator new分配原始内存然后通过placement new在指定地址构造对象并显式调用析构函数来销毁对象。这与真实 STL 采用std::allocator的原理一致但代码更直观。真实std::vector的关键特性连续内存元素存储在堆heap中逻辑地址连续。动态扩容当size capacity时分配更大内存通常capacity * 2。分离容量与大小size表示实际元素个数capacity表示已分配内存能容纳的元素数。只构造已使用的元素未使用的内存保持未初始化状态避免不必要的构造开销。迭代器直接使用指针T*即可。完整代码#include iostream #include algorithm // for std::swap #include stdexcept // for std::out_of_range template typename T class SimpleVector { private: T* data; // 指向动态数组的指针 size_t sz; // 当前元素个数 size_t cap; // 当前容量可容纳元素数 // 核心工具函数分配新内存将旧元素拷贝过去释放旧内存 void reallocate(size_t new_cap) { // 1. 分配原始内存不构造对象 T* new_data static_castT*(::operator new(new_cap * sizeof(T))); // 2. 用拷贝构造将旧元素逐个放置到新内存 for (size_t i 0; i sz; i) { new (new_data i) T(data[i]); // placement new } // 3. 析构旧元素释放对象资源 for (size_t i 0; i sz; i) { data[i].~T(); } // 4. 释放旧内存 ::operator delete(data); // 5. 更新指针和容量 data new_data; cap new_cap; } public: // ---------- 构造函数 / 析构函数 ---------- SimpleVector() : data(nullptr), sz(0), cap(0) {} // 拷贝构造 SimpleVector(const SimpleVector other) : data(nullptr), sz(other.sz), cap(other.sz) { if (sz 0) { data static_castT*(::operator new(sz * sizeof(T))); for (size_t i 0; i sz; i) { new (data i) T(other.data[i]); } } } // 拷贝赋值copy-and-swap 惯用法 SimpleVector operator(SimpleVector other) { swap(*this, other); // 这里需要下面的 friend swap return *this; } // 析构 ~SimpleVector() { clear(); // 析构所有元素 ::operator delete(data); // 释放原始内存 } // ---------- 容量相关 ---------- size_t size() const { return sz; } size_t capacity() const { return cap; } bool empty() const { return sz 0; } // 预分配容量若新容量大于当前容量 void reserve(size_t new_cap) { if (new_cap cap) { reallocate(new_cap); } } // 改变大小若小于当前大小则截断否则用 value 填充 void resize(size_t new_size, const T value T()) { if (new_size sz) { // 截断析构多出的元素 for (size_t i new_size; i sz; i) { data[i].~T(); } sz new_size; } else if (new_size sz) { reserve(new_size); // 保证容量足够 for (size_t i sz; i new_size; i) { new (data i) T(value); // 构造新元素 } sz new_size; } } // ---------- 元素访问 ---------- T operator[](size_t index) { return data[index]; } const T operator[](size_t index) const { return data[index]; } T at(size_t index) { if (index sz) throw std::out_of_range(index out of range); return data[index]; } const T at(size_t index) const { if (index sz) throw std::out_of_range(index out of range); return data[index]; } // ---------- 修改器 ---------- void push_back(const T value) { if (sz cap) { size_t new_cap (cap 0) ? 1 : cap * 2; reallocate(new_cap); } new (data sz) T(value); // 在末尾构造 sz; } void pop_back() { if (empty()) throw std::out_of_range(pop_back on empty vector); --sz; data[sz].~T(); // 析构最后一个元素 } void clear() { for (size_t i 0; i sz; i) { data[i].~T(); } sz 0; } // ---------- 迭代器直接使用指针 ---------- T* begin() { return data; } T* end() { return data sz; } const T* begin() const { return data; } const T* end() const { return data sz; } // ---------- 交换用于拷贝赋值和移动 ---------- friend void swap(SimpleVector a, SimpleVector b) noexcept { using std::swap; swap(a.data, b.data); swap(a.sz, b.sz); swap(a.cap, b.cap); } };关键点解析1. 内存管理堆区与逻辑地址::operator new分配的是原始内存未构造对象返回void*我们强转为T*。这块内存位于进程的堆区heap与你之前学习的进程地址空间完全对应。使用placement newnew (address) T(args)在已分配的内存上构造对象而不是重新申请内存。这避免了对未使用区域调用默认构造函数提高了效率对复杂类型尤其重要。析构时我们显式调用析构函数data[i].~T()来释放对象内部资源如动态分配的成员然后再用::operator delete释放整个原始内存块。2. 扩容策略reallocate当size capacity时触发扩容。新容量通常翻倍或从 1 开始这是为了平摊push_back的时间复杂度到 O(1)。reallocate会分配新内存。将旧元素拷贝构造到新内存如果类型支持移动可进一步优化为移动构造。析构并释放旧内存。更新指针和容量。注意如果T的拷贝构造函数可能抛出异常上述reallocate不是异常安全的旧数据可能丢失。真实 STL 会先复制到新内存成功后再释放旧内存。为简化此处忽略异常安全但你可以在此基础上改进。3. 拷贝赋值Copy-and-Swapoperator接受参数为值传递这会在参数构造时调用拷贝构造或移动构造。然后通过swap交换当前对象和临时对象临时对象析构时释放原来的资源。这种写法简洁且自动处理了自赋值和异常安全。4. 迭代器与边界检查begin()/end()返回T*这正是连续内存的优势 —— 指针就是天然迭代器。operator[]不检查边界at()会抛异常与标准库行为一致。下面是测试用例int main() { SimpleVectorint v; v.push_back(1); v.push_back(2); v.push_back(3); std::cout size v.size() , capacity v.capacity() \n; for (auto it v.begin(); it ! v.end(); it) { std::cout *it ; } std::cout \n; v.pop_back(); std::cout after pop: size v.size() \n; v.resize(5, 0); std::cout after resize(5,0): ; for (size_t i 0; i v.size(); i) { std::cout v[i] ; } std::cout \n; try { std::cout v.at(10) \n; } catch (const std::out_of_range e) { std::cerr caught: e.what() \n; } return 0; }输出结果size 3, capacity 4 1 2 3 after pop: size 2 after resize(5,0): 1 2 0 0 0 caught: index out of range

相关新闻

最新新闻

Laravel Nova安装配置全链路解析:从私有源到RBAC权限

Laravel Nova安装配置全链路解析:从私有源到RBAC权限

1. 项目概述:这不是“装个软件”,而是给 Laravel 应用装上一套企业级后台操作系统Nova 不是 Laravel 的一个普通插件,它是一套完整、可定制、生产就绪的管理后台框架——你可以把它理解成 Laravel 生态里的“AdminJS Strapi Retool”三合一…

2026/7/11 3:35:43
大模型Top 1威胁和智能体Top 1威胁有何关系

大模型Top 1威胁和智能体Top 1威胁有何关系

图注:大模型 Top 1「提示注入」与智能体 Top 1「目标劫持」——攻击本质同源,防御需同步进化。 过去两年,谈大模型安全,几乎绕不开一句话:提示注入(Prompt Injection)。OWASP《大语言模型应用 Top 10 2025》里,它稳居 LLM01。 而到了 2026 年的《智能体应用 Top 10》,…

2026/7/11 3:35:43
STM32 直流电机速度环 PID 实战:增量式 vs 位置式,3 组参数调优对比

STM32 直流电机速度环 PID 实战:增量式 vs 位置式,3 组参数调优对比

STM32 直流电机速度环 PID 实战:增量式 vs 位置式,3 组参数调优对比在智能车竞赛和嵌入式开发中,直流电机的精确控制一直是核心挑战。去年调试一辆四轮驱动智能车时,我们发现同样的PID参数在不同赛道段表现迥异——直道需要快速响…

2026/7/11 3:35:43
TADF材料设计实战:ΔEST < 0.2 eV 分子实现 100% IQE 的 3 个关键策略

TADF材料设计实战:ΔEST < 0.2 eV 分子实现 100% IQE 的 3 个关键策略

TADF材料设计实战&#xff1a;ΔEST < 0.2 eV 分子实现 100% IQE 的 3 个关键策略在有机发光二极管&#xff08;OLED&#xff09;和光电材料领域&#xff0c;热活化延迟荧光&#xff08;TADF&#xff09;材料因其理论上可实现100%内量子效率&#xff08;IQE&#xff09;而备…

2026/7/11 3:35:43
3 种空间可达性方法对比:两步移动搜索法 vs 核密度 vs 重力模型

3 种空间可达性方法对比:两步移动搜索法 vs 核密度 vs 重力模型

空间可达性分析三大核心方法实战对比&#xff1a;从理论到选型指南 当城市规划师需要评估新建医院的覆盖范围&#xff0c;或社会学家研究教育资源分配公平性时&#xff0c;一个关键问题始终存在&#xff1a;如何量化"空间可达性"&#xff1f;这种测量不仅关乎地理距离…

2026/7/11 3:35:43
OpenClaw OAuth接入ChatGPT Plus:合规授权与安全网关实践

OpenClaw OAuth接入ChatGPT Plus:合规授权与安全网关实践

1. 项目本质与核心价值再定义&#xff1a;这不是“免API Key”&#xff0c;而是OAuth协议的合规落地实践看到标题里“无需 API Key&#xff0c;直接使用 ChatGPT Plus 授权登录”&#xff0c;很多人的第一反应是“又一个免密登录的野路子”——这恰恰是最大的认知陷阱。我用 Op…

2026/7/11 3:30:42

月新闻