如何实现支持无默认构造函数对象的自定义CircularBuffer
问题描述
我发现std::vector可以适配没有默认构造函数的对象,但自己实现的CircularBuffer却做不到。
定义类A如下:
class A { public: A() = delete; A(const int &x) :x(x) {} private: int x; };
CircularBuffer的部分实现代码如下:
template <typename T, int N> //default constructor of the vector CircularBuffer<T,N>::CircularBuffer() { Size = 0; Capacity =N; Array = new T[Capacity]; Start = 0; End = 0; } template <typename T, int N> CircularBuffer<T,N>::CircularBuffer(const CircularBuffer& rhs) //copy constructor of the vector { Size = rhs.Size; Capacity = rhs.Capacity; Array = new T[Capacity]; Start = rhs.Start; End = rhs.End; for(int i=0;i<Capacity;i++) { this->Array[i] = rhs.Array[i]; } } template <typename T, int N> //move constructor of the vector CircularBuffer<T,N>::CircularBuffer(CircularBuffer&& rhs) { rhs.Swap(*this); } template <typename T, int N> void CircularBuffer<T,N>:: Swap(CircularBuffer &source) { swap(Size,source.Size); swap(Capacity,source.Capacity); swap(Start,source.Start); swap(End,source.End); swap(Array,source.Array); }
当尝试创建CircularBuffer<A,3>对象时:
CircularBuffer<A,3> v;
出现编译错误:error: use of deleted function ‘A::A()’
问题原因
核心问题出在new T[Capacity]这行代码:
- 数组形式的
new(new T[N])会强制默认构造N个T类型对象,这要求T必须有可访问的默认构造函数,而类A显式删除了默认构造函数,直接触发编译错误。 std::vector之所以能适配无默认构造的类型,是因为它把内存分配和对象构造拆分成了两步:先通过分配器申请原始内存,等到真正需要添加元素(比如push_back)时,才用放置new在指定内存位置构造对象,不会一开始就批量默认构造元素。
解决方案
要让CircularBuffer支持无默认构造函数的类型,需要模仿std::vector的内存管理逻辑,分离内存分配与对象构造:
1. 改用原始内存分配
放弃new T[Capacity],改用operator new分配原始内存(不触发对象构造):
// 分配能容纳Capacity个T的原始内存 Array = static_cast<T*>(operator new(sizeof(T) * Capacity));
2. 手动控制对象的构造与销毁
- 添加元素:使用放置new在已分配的内存上构造对象,比如向环形缓冲区添加A类型元素:
// 在Array的End位置构造A(5) new (&Array[End]) T(5); End = (End + 1) % Capacity; Size++; - 销毁元素:手动调用对象的析构函数,再释放原始内存:
// 销毁所有已构造的元素 for (int i = 0; i < Size; ++i) { int idx = (Start + i) % Capacity; Array[idx].~T(); } // 释放原始内存 operator delete(Array);
3. 修改现有构造函数
默认构造函数
template <typename T, int N> CircularBuffer<T,N>::CircularBuffer() : Size(0), Capacity(N), Start(0), End(0) { // 只分配原始内存,不构造对象 Array = static_cast<T*>(operator new(sizeof(T) * Capacity)); }
拷贝构造函数
不能再用new T[Capacity],需先分配原始内存,再逐个拷贝构造已存在的元素:
template <typename T, int N> CircularBuffer<T,N>::CircularBuffer(const CircularBuffer& rhs) : Size(rhs.Size), Capacity(rhs.Capacity), Start(rhs.Start), End(rhs.End) { Array = static_cast<T*>(operator new(sizeof(T) * Capacity)); // 只拷贝缓冲区中已存在的Size个元素,而非全部Capacity个 for (int i = 0; i < Size; ++i) { int src_idx = (rhs.Start + i) % rhs.Capacity; int dest_idx = (Start + i) % Capacity; new (&Array[dest_idx]) T(rhs.Array[src_idx]); } }
补充析构函数
必须手动销毁已构造的对象,再释放内存:
template <typename T, int N> CircularBuffer<T,N>::~CircularBuffer() { // 销毁所有已构造的元素 for (int i = 0; i < Size; ++i) { int idx = (Start + i) % Capacity; Array[idx].~T(); } // 释放原始内存 operator delete(Array); }
额外注意事项
- 拷贝赋值运算符、移动赋值运算符也需要对应修改,确保正确处理对象的构造与销毁逻辑。
- 如果
CircularBuffer支持resize、clear等操作,也要遵循“先析构旧对象,再构造新对象”的规则。
内容的提问来源于stack exchange,提问作者Alex Seong
相关产品推荐
相关产品推荐

