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

C++模板类自定义Vector嵌套存储时析构崩溃如何解决

自定义模板Vector嵌套存储时析构崩溃修复

问题现象

尝试基于模板类实现自定义Vector类,预期支持将Vector<int>类型对象存入Vector<Vector<int>>类型变量,但程序运行时始终在析构函数逻辑处触发崩溃。
初始实现代码:

#include <iostream>
#include <string>

template <typename T>
class Vector {

    T* data;
    int capacity;
    int length;

public:

    typedef T value_type;

    Vector() {}

    Vector(int n) : data(new T[n]), capacity(n), length(0) {}

    void push_back(T input) { 
        data[length++] = input;
    }

    T operator[](int i) { return data[i]; }

    virtual ~Vector() { if (data) delete[] data; }
};

int main() {
    Vector<Vector<int>> v(3);
    Vector<int> vv(4);
    v.push_back(vv);
}

初步排查时推测问题源于拷贝构造函数缺失,直观表现为v的析构时机早于vv;如果直接注释掉析构函数的内存释放逻辑,程序可以正常运行,但这种处理方式会造成内存泄漏,不符合内存管理规范。
后续尝试编写自定义拷贝构造函数时写了如下错误实现:

Vector(const T& other) { 

}

编译时直接抛出ambiguous overloading(歧义重载)错误,排查后发现该写法参数类型不匹配,类模板的类型参数T与拷贝构造函数要求的参数类型不符。


根因分析

  • 违反C++类设计的三法则(Rule of Three):类中自定义了析构函数负责释放堆内存,但未正确实现拷贝构造函数和拷贝赋值运算符,编译器默认生成的拷贝逻辑只会对指针成员做浅拷贝,会导致多个Vector对象指向同一块堆内存,对象析构时同一块内存被重复释放,触发段错误。
  • 默认构造函数未初始化成员变量:无参构造Vector() {}没有给data、capacity、length赋初始值,data会成为野指针,析构时执行if(data)判断会读取未初始化的随机内存地址,直接触发未定义行为。
  • 拷贝构造函数签名完全错误:Vector<T>的拷贝构造参数必须是当前类自身类型的const引用,即const Vector<T>&。你写的Vector(const T& other)本质是一个支持用T类型对象隐式构造Vector的转换构造函数,和已有的单参数构造函数在隐式转换场景下产生调用歧义,根本不具备拷贝构造的能力。
  • 其余细节问题:operator[]返回的是元素值拷贝,无法支持修改容器内元素;push_back未做容量判断,超过预分配容量会写越界。

修复方案

修正后的完整可运行代码如下:

#include <iostream>
#include <utility> // for std::swap

template <typename T>
class Vector {
    // 类内默认初始化成员,从根源避免野指针
    T* data = nullptr;
    size_t capacity = 0;
    size_t length = 0;

public:
    typedef T value_type;

    // 默认构造函数
    Vector() = default;

    // 预分配容量构造
    Vector(size_t n) : data(new T[n]()), capacity(n), length(0) {}

    // 正确实现拷贝构造:做深拷贝,为新对象单独申请内存
    Vector(const Vector<T>& other)
        : data(new T[other.capacity]),
          capacity(other.capacity),
          length(other.length) {
        for (size_t i = 0; i < length; ++i) {
            data[i] = other.data[i];
        }
    }

    // 拷贝赋值运算符,采用copy-and-swap写法保证异常安全、规避自赋值问题
    Vector<T>& operator=(Vector other) {
        std::swap(data, other.data);
        std::swap(capacity, other.capacity);
        std::swap(length, other.length);
        return *this;
    }

    void push_back(T input) {
        // 注:此处省略自动扩容逻辑,仅适配当前demo预分配足够容量的场景
        // 生产环境使用需要补充:当length == capacity时,申请2倍大小新内存,迁移元素后释放旧内存
        data[length++] = input;
    }

    // 重载[]返回引用,支持元素修改,同时补充const版本适配const对象调用
    T& operator[](size_t i) { return data[i]; }
    const T& operator[](size_t i) const { return data[i]; }

    ~Vector() {
        // C++标准规定delete[]空指针是安全操作,无需额外判空
        delete[] data;
    }
};

int main() {
    Vector<Vector<int>> v(3);
    Vector<int> vv(4);
    vv[0] = 1;
    v.push_back(vv);
    std::cout << v[0][0] << std::endl; // 正常输出1
    return 0;
}

核心修改点:

  • 所有构造路径都保证成员变量被正确初始化,默认构造通过类内初始值把data设为空指针,避免野指针问题。
  • 修正拷贝构造函数签名,实现深拷贝逻辑:拷贝对象时单独申请新的堆内存,逐个复制源对象的元素,彻底避免多个对象共享同一块内存导致的重复释放。
  • 补充拷贝赋值运算符,采用copy-and-swap实现,代码简洁且天然规避自赋值风险、满足异常安全要求。
  • 修正operator[]的返回值为引用类型,支持修改容器内元素,同时补充const重载适配只读场景。
  • 移除析构函数中冗余的空指针判断,简化代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 12:36:15