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

如何创建存储自定义struct的动态数组且避免元素被意外析构

问题根本原因

你的代码触发段错误的核心是struct T没有遵循C++的「三五法则」,默认生成的拷贝赋值运算符执行的是浅拷贝:当你执行array[i] = t时,array[i].heapValue和t.heapValue会指向同一块堆内存,当临时对象t离开作用域被析构时,这块内存会被直接释放,导致array[i].heapValue成为悬空指针,后续访问就会触发段错误。

可行解决方案

方案1:为T实现正确的拷贝语义(最通用)

给T补充自定义的拷贝构造函数、拷贝赋值运算符,必要时补充移动构造和移动赋值运算符,从根源上避免浅拷贝问题。修改后你可以直接使用std::vector<T>或者T*动态数组,不需要额外的指针包装层,且元素是连续存储的,缓存友好性更好,访问性能远高于指针数组。

#include <algorithm>
struct T {
    int* heapValue;
    int size; // 补充记录数组大小,方便拷贝
    T(){
        size = 3;
        heapValue = new int[size];
    }
    // 自定义带参构造可保留原有逻辑
    T(int a, int b, int args) {
        size = 3;
        heapValue = new int[size];
        // 你的初始化逻辑
    }
    void do_something() {
        // 你的业务逻辑
    }
    // 拷贝构造
    T(const T& other) {
        size = other.size;
        heapValue = new int[size];
        std::copy(other.heapValue, other.heapValue + size, heapValue);
    }
    // 拷贝赋值
    T& operator=(const T& other) {
        if (this == &other) return *this;
        delete[] heapValue;
        size = other.size;
        heapValue = new int[size];
        std::copy(other.heapValue, other.heapValue + size, heapValue);
        return *this;
    }
    // 移动构造(可选,提升性能)
    T(T&& other) noexcept {
        heapValue = other.heapValue;
        size = other.size;
        other.heapValue = nullptr;
        other.size = 0;
    }
    // 移动赋值(可选,提升性能)
    T& operator=(T&& other) noexcept {
        if (this == &other) return *this;
        delete[] heapValue;
        heapValue = other.heapValue;
        size = other.size;
        other.heapValue = nullptr;
        other.size = 0;
        return *this;
    }
    ~T(){
        delete[] heapValue;
    }
};

int main() {
    int N = 5;
    // 直接用std::vector<T>更安全,不需要手动释放数组
    std::vector<T> array(N);
    // 如果你坚持用T*动态数组,也可以正常运行:T* array = new T[N];

    for (int i = 0; i < N; i++)
    {
      T t = T(123, 456, 789);
      t.do_something();
      array[i] = t;
    } 
    // 这里t销毁不会影响array[i],因为是深拷贝

    int x = array[0].heapValue[0]; // 正常访问不会报错
    // vector销毁时会自动释放所有元素和数组内存,不需要手动操作
    // 如果用T*数组,执行delete[] array即可,不需要遍历释放元素
    return 0;
}

方案2:使用智能指针容器(无需修改T定义)

如果你不想修改T的原有实现,可以用std::vector<std::unique_ptr<T>>替代裸指针数组,既不需要手动遍历释放元素,也不会出现T**的二维数组误解,内存管理完全自动:

#include <vector>
#include <memory>
int main() {
    int N = 5;
    std::vector<std::unique_ptr<T>> array(N);
    for (int i = 0; i < N; i++)
    {
      auto t = std::make_unique<T>(123, 456, 789);
      t->do_something();
      array[i] = std::move(t);
    } 
    int x = array[0]->heapValue[0]; // 正常访问
    // 无需任何手动释放操作,vector销毁时自动处理所有内存
    return 0;
}

方案3:直接使用移动语义赋值

如果T的拷贝成本极高,你也可以在不补充拷贝语义的情况下,给T实现移动构造和移动赋值,赋值时转移内存所有权,避免临时对象析构释放内存:

// 只需要给T补充移动构造和移动赋值,不需要拷贝相关实现
for (int i = 0; i < N; i++)
{
  T t = T(123, 456, 789);
  t.do_something();
  array[i] = std::move(t); // 转移所有权,t析构时不会释放已经转移的内存
}

方案对比

  • 方案1是最符合C++常规开发规范的实现,修改后T的行为和内置类型一致,使用门槛最低
  • 方案2适合无法修改T原有定义的场景,比手动管理T**安全得多,也没有额外的性能损耗
  • 方案3适合大对象场景,性能最优,但需要注意移动后原对象不可再使用

内容的提问来源于stack exchange,提问作者Gaberocksall

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 00:45:03