如何存储数量可变且不可默认构造、复制、移动的对象?
问题:存储不可移动/复制、非默认构造的运行时数量对象
我需要一块连续内存存储运行时才知晓数量的Data对象,但这些对象:
- 不可默认构造
- 不可复制、不可移动(因持有资源和外部内存引用,且我希望显式禁用这些操作避免误操作)
背景:开发数据处理程序,程序启动初期输入会指定Data对象的创建数量,每个Data持有资源并引用输入对象的内存,运行时需要反复遍历这些对象执行任务。
原本用std::vector预留内存后调用emplace_back实现连续存储,但为调试添加Data析构函数后,std::vector依赖的移动构造函数被隐式删除,导致编译失败。实际上这些移动构造函数从未被调用过,只是生成了无用的二进制代码,我更倾向于直接删除这些操作符。
试过的方案:
- 若
Data可默认构造并后续设置输入指针,可使用std::unique_ptr<Data[]>,但目前Data无法默认构造 - 尝试过eastl::fixed_vector,但它需要编译时指定容量,且要求对象可移动
方案1:手动管理内存 + std::aligned_storage
直接控制原始内存,手动构造/析构对象,完全规避复制/移动需求,确保对象连续存储。
代码示例
#include <memory> #include <type_traits> class Input {}; // 示例输入类 class Data { public: explicit Data(const Input& input) : input_(&input) {} ~Data() = default; // 显式禁用复制、移动操作 Data(const Data&) = delete; Data& operator=(const Data&) = delete; Data(Data&&) = delete; Data& operator=(Data&&) = delete; // 业务处理方法 void process() {} private: const Input* input_; // 其他资源成员 }; // 自定义连续存储容器 class DataBuffer { public: DataBuffer(size_t count, const Input& input) : storage_(std::make_unique<std::aligned_storage_t<sizeof(Data), alignof(Data)>[]>(count)), count_(count) { // 原地构造每个Data对象 for (size_t i = 0; i < count_; ++i) { new (get_ptr(i)) Data(input); } } ~DataBuffer() { // 手动析构每个Data对象 for (size_t i = 0; i < count_; ++i) { get_ptr(i)->~Data(); } } // 禁用容器的复制/移动(可选,根据需求调整) DataBuffer(const DataBuffer&) = delete; DataBuffer& operator=(const DataBuffer&) = delete; DataBuffer(DataBuffer&&) = delete; DataBuffer& operator=(DataBuffer&&) = delete; // 随机访问接口 Data& operator[](size_t idx) { return *get_ptr(idx); } const Data& operator[](size_t idx) const { return *get_ptr(idx); } // 迭代器支持(方便范围for遍历) Data* begin() { return get_ptr(0); } Data* end() { return get_ptr(count_); } const Data* begin() const { return get_ptr(0); } const Data* end() const { return get_ptr(count_); } size_t size() const { return count_; } private: // 转换内存指针为Data对象指针 Data* get_ptr(size_t idx) { return reinterpret_cast<Data*>(&storage_[idx]); } const Data* get_ptr(size_t idx) const { return reinterpret_cast<const Data*>(&storage_[idx]); } std::unique_ptr<std::aligned_storage_t<sizeof(Data), alignof(Data)>[]> storage_; size_t count_; }; // 使用示例 int main() { Input input; const size_t data_count = 100; // 运行时确定的数量 DataBuffer buffer(data_count, input); for (auto& data : buffer) { data.process(); } }
优缺点
- 优点:完全控制内存布局,确保对象连续存储;无需
Data支持复制/移动;运行时动态指定数量。 - 缺点:需手动管理对象构造/析构,代码量略多;需自行实现容器基本功能(如迭代器)。
方案2:使用Boost容器库的boost::container::static_vector
若可引入Boost库,boost::container::static_vector支持运行时指定容量(非编译时固定),且在不扩容的场景下无需对象可移动。
代码示例
#include <boost/container/static_vector.hpp> class Input {}; class Data { /* 同方案1,禁用复制移动 */ }; int main() { Input input; const size_t data_count = 100; // 0表示不限制编译时容量,运行时动态分配 boost::container::static_vector<Data, 0> buffer; buffer.reserve(data_count); for (size_t i = 0; i < data_count; ++i) { buffer.emplace_back(input); } for (auto& data : buffer) { data.process(); } }
优缺点
- 优点:自带容器的所有便利功能(迭代器、size、访问方法等);无需手动管理构造析构。
- 缺点:依赖Boost库;若后续需扩容,仍要求对象可移动(但你的场景无需扩容)。
方案3:std::vector<std::unique_ptr<Data>>(妥协方案)
若可接受指针连续存储而非对象本身连续,此方案实现最简单,但对象实际存储在堆上,缓存友好性稍差。
代码示例
#include <vector> #include <memory> class Input {}; class Data { /* 同方案1,禁用复制移动 */ }; int main() { Input input; const size_t data_count = 100; std::vector<std::unique_ptr<Data>> buffer; buffer.reserve(data_count); for (size_t i = 0; i < data_count; ++i) { buffer.emplace_back(std::make_unique<Data>(input)); } for (auto& data_ptr : buffer) { data_ptr->process(); } }
优缺点
- 优点:实现最简单,无需额外代码;完全兼容
Data的禁用复制移动。 - 缺点:对象本身不连续,缓存命中率可能降低;多一层指针间接访问,性能略有损耗。
内容的提问来源于stack exchange,提问作者Lambo
相关产品推荐
相关产品推荐

