You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现支持无默认构造函数对象的自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 05:15:38