代码报错‘pointer being freed was not allocated’的原因及修复方法
自定义C++ Vector类的内存错误修复
错误日志
12 SIZE: 3 CAPACITY: 4 12 SIZE: 4 CAPACITY: 4 DESTRUCTOR proj1(53055,0x10121c580) malloc: *** error for object 0x600001ac1110: pointer being freed was not allocated proj1(53055,0x10121c580) malloc: *** set a breakpoint in malloc_error_break to debug zsh: abort /Users/mascai/root_folder/dev/projects/03_cpp_examples/02_ide/build/proj1
问题代码
template<class T> class Vector { public: Vector() = default; Vector(size_t size) : size_(size), capacity_(size * 2) { data_= new T[capacity_]; } Vector(size_t size, const T val) : size_(size), capacity_(size * 2) { data_= new T[capacity_]; for (size_t i = 0; i < size_; ++i) { data_[i] = val; } } Vector(const Vector& other) { data_ = other.data_; size_ = other.size_; capacity_ = other.capacity_; } const Vector& operator=(const Vector<T>& other) { data_ = other.data_; size_ = other.size_; capacity_ = other.capacity_; return *this; } ~Vector() { cout << "DESTRUCTOR" << endl; if (data_) { delete[] data_; } size_ = 0; capacity_ = 0; } T operator[](size_t idx) const { if (idx < size_) { return *(data_ + idx); } throw std::invalid_argument("Inde out of range"); } void push_back(const T& val) { size_ += 1; if (size_ > capacity_) { capacity_ *= 2; T* new_data = new T[capacity_]; for (int i = 0; i < size_ - 1; ++i) { *new_data++ = *data_++; } delete[] data_; data_ = new_data; } else { *(data_ + size_ - 1) = val; } } size_t size() const { return size_; } size_t capacity() const { return capacity_; } private: T* data_ = nullptr; size_t size_ = 0; size_t capacity_ = 0; }; int main() { Vector<int> v(2, 111); assert(v[0] == 111); v.push_back(12); cout << v[2] << " SIZE: " << v.size() << " CAPACITY: " << v.capacity() << endl; v.push_back(12); cout << v[3] << " SIZE: " << v.size() << " CAPACITY: " << v.capacity() << endl; v.push_back(13); }
错误原因分析
- push_back指针非法偏移:扩容拷贝元素时,
*new_data++ = *data_++会修改data_的指向,使其从数组首地址移到末尾。后续delete[] data_释放的不是最初分配的首地址,触发内存释放错误。 - 浅拷贝的双重释放隐患:拷贝构造和赋值运算符直接复制指针,多个Vector对象共享同一块内存,析构时会重复释放已释放的内存。
- size递增时机不合理:先递增
size_再判断扩容,拷贝元素时需用size_-1获取原元素数量,增加出错概率。
修复步骤
1. 修复push_back的指针偏移问题
拷贝元素时不移动原始data_指针,改用索引访问,同时调整size递增时机:
void push_back(const T& val) { if (size_ >= capacity_) { // 空容器第一次扩容时初始化容量为1 capacity_ = capacity_ == 0 ? 1 : capacity_ * 2; T* new_data = new T[capacity_]; for (size_t i = 0; i < size_; ++i) { new_data[i] = data_[i]; } delete[] data_; data_ = new_data; } data_[size_] = val; size_++; }
2. 实现深拷贝的拷贝构造函数
Vector(const Vector& other) : size_(other.size_), capacity_(other.capacity_) { data_ = new T[capacity_]; for (size_t i = 0; i < size_; ++i) { data_[i] = other.data_[i]; } }
3. 用拷贝交换惯用法实现赋值运算符
这种写法更简洁安全,自动处理内存管理:
Vector& operator=(Vector other) { std::swap(data_, other.data_); std::swap(size_, other.size_); std::swap(capacity_, other.capacity_); return *this; }
4. 修复operator[]的细节
添加非const版本支持修改元素,同时修正拼写错误:
// 非const版本,支持修改元素 T& operator[](size_t idx) { if (idx < size_) { return data_[idx]; } throw std::invalid_argument("Index out of range"); } // const版本,只读访问 const T& operator[](size_t idx) const { if (idx < size_) { return data_[idx]; } throw std::invalid_argument("Index out of range"); }
修复后的完整代码
#include <iostream> #include <cassert> #include <algorithm> template<class T> class Vector { public: Vector() = default; Vector(size_t size) : size_(size), capacity_(size * 2) { data_ = new T[capacity_]; } Vector(size_t size, const T val) : size_(size), capacity_(size * 2) { data_ = new T[capacity_]; for (size_t i = 0; i < size_; ++i) { data_[i] = val; } } Vector(const Vector& other) : size_(other.size_), capacity_(other.capacity_) { data_ = new T[capacity_]; for (size_t i = 0; i < size_; ++i) { data_[i] = other.data_[i]; } } Vector& operator=(Vector other) { std::swap(data_, other.data_); std::swap(size_, other.size_); std::swap(capacity_, other.capacity_); return *this; } ~Vector() { std::cout << "DESTRUCTOR" << std::endl; delete[] data_; // nullptr时delete[]安全,无需判断 size_ = 0; capacity_ = 0; } T& operator[](size_t idx) { if (idx < size_) { return data_[idx]; } throw std::invalid_argument("Index out of range"); } const T& operator[](size_t idx) const { if (idx < size_) { return data_[idx]; } throw std::invalid_argument("Index out of range"); } void push_back(const T& val) { if (size_ >= capacity_) { capacity_ = capacity_ == 0 ? 1 : capacity_ * 2; T* new_data = new T[capacity_]; for (size_t i = 0; i < size_; ++i) { new_data[i] = data_[i]; } delete[] data_; data_ = new_data; } data_[size_] = val; size_++; } size_t size() const { return size_; } size_t capacity() const { return capacity_; } private: T* data_ = nullptr; size_t size_ = 0; size_t capacity_ = 0; }; int main() { Vector<int> v(2, 111); assert(v[0] == 111); v.push_back(12); std::cout << v[2] << " SIZE: " << v.size() << " CAPACITY: " << v.capacity() << std::endl; v.push_back(12); std::cout << v[3] << " SIZE: " << v.size() << " CAPACITY: " << v.capacity() << std::endl; v.push_back(13); std::cout << v[4] << " SIZE: " << v.size() << " CAPACITY: " << v.capacity() << std::endl; }
内容的提问来源于stack exchange,提问作者mascai
相关产品推荐
相关产品推荐

