从零实现C++动态数组:append函数中delete[]触发段错误求助
问题描述
我正在完成一项作业,从零开始实现C++的vector(动态数组),但一整天都遇到问题。append函数存在故障:最初无法接收string输入,现在已能向动态列表添加第一个string,但添加第二个string时,temp数组成功添加该元素,然而第二次delete[]现有的data数组(即之前的temp数组)时触发了段错误。我添加了大量打印语句来定位问题。
由于string是对象,我尝试使用placement new实例化对象块。以下是相关代码文件:
DynamicList.cpp
#include <iostream> #include <string> #include "DynamicList.hpp" #include <algorithm> //Constructor template<typename T> DynamicList<T>::DynamicList(){ size = 0; data = new T[size]; } //Destructor template<typename T> DynamicList<T>::~DynamicList(){ std::cout << "Deleting array of size: " << getSize() << std::endl; delete[] data; } //Overloading operators template<typename T> DynamicList<T> DynamicList<T>::operator=(DynamicList<T>& obj){ obj.size = size; std::copy(data, data + size, obj.data); } template<typename T> bool DynamicList<T>::operator==(DynamicList<T>& obj){ if(size != obj.size){ return false; } else{ for(int i = 0; i < size; i++){ if(obj.data[i] != data[i]){ return false; } } return true; } } template<typename T> bool DynamicList<T>::operator!=(DynamicList<T>& obj){ return !(DynamicList<T>::operator==(obj)); } template<typename T> T DynamicList<T>::operator[](int index){ for(int i = 0; i < size; i++){ if(i == index){ return data[i]; } } } //Returns the size of the list template<typename T> int DynamicList<T>::getSize(){ return size; } //Add element to the end of list. template<typename T> bool DynamicList<T>::append(T item){ std::cout << data << std::endl; std::cout << item << std::endl; T* temp = static_cast<T*>(new T(sizeof(T) * (size + 1), alignof(T))); if(size > 1){ for(int i = 0; i < size; i++){ T* somePtr = new (&temp[i]) T; *somePtr = data[i]; } } T* itemPtr = new (&temp[size]) T; *itemPtr = item; std::cout << "Ptr made in array slot.\n"; std::cout << temp << std::endl; size++; std::cout << "Size increased.\n"; std::cout << data << std::endl; std::cout << "AAAAAA\n"; delete[] data; std::cout << "BBBBB\n"; data = temp; std::cout << data[size - 1] << std::endl; return true; }
DynamicList.hpp
#include <iostream> #include <string> #ifndef DYNAMICLIST_H #define DYNAMICLIST_H template<typename T> class DynamicList{ private: int size; T* data; public: //constructor DynamicList(); //destructor ~DynamicList(); //Operator overloaders DynamicList operator=(DynamicList& obj); bool operator==(DynamicList& obj); bool operator!=(DynamicList& obj); T operator[](int index); //Returns the size of the data array int getSize(); //Add an item to the end of the list bool append(T item); //Remove an item at an index bool remove(int index); }; #endif
main.cpp
#include <iostream> #include <string> #include "DynamicList.hpp" #include "DynamicList.cpp" int main(){ DynamicList<std::string> stringList; stringList.append("test"); stringList.append("test2"); return 0; }
Makefile
all: DynamicList.hpp DynamicList.cpp main.cpp g++ -c DynamicList.cpp g++ -c main.cpp g++ DynamicList.o main.o -o exec run: all ./exec clean: rm -f *.o rm -f exec
错误输出
./exec 0x55c688824eb8 test Ptr made in array slot. 0x55c6888252f0 Size increased. 0x55c688824eb8 AAAAAA BBBBB test 0x55c6888252f0 test2 Ptr made in array slot. 0x55c688824eb0 Size increased. 0x55c6888252f0 AAAAAA make: *** [makefile:8: run] Segmentation fault (core dumped)
原因分析
- 内存分配逻辑完全错误:
append中创建temp的代码new T(sizeof(T)*(size+1), alignof(T)),并不是分配能容纳size+1个T对象的内存块,而是调用std::string的构造函数,传入整数参数创建了单个string对象。后续访问temp[i](i≥1)属于越界访问,直接破坏内存结构,最终导致delete[] data时触发段错误。 - placement new使用错误:条件
size>1导致size=1时不会拷贝已有元素;同时,即使拷贝,也没有正确处理原对象的析构逻辑,且内存越界已经导致内存 corruption。 - 其他潜在问题:赋值运算符
operator=逻辑颠倒(修改传入的obj而非当前对象)且无返回值;operator[]未处理越界情况;构造函数new T[0]虽合法但冗余;析构函数未先析构对象就直接释放数组内存。
解决方案
1. 修正append核心逻辑
实现动态数组需先分配原始内存,再用placement new构造对象,最后析构原对象并释放内存:
template<typename T> bool DynamicList<T>::append(T item){ // 分配能容纳size+1个T的原始内存 void* raw_mem = operator new(sizeof(T) * (size + 1)); T* temp = static_cast<T*>(raw_mem); // 拷贝构造已有元素 for(int i = 0; i < size; ++i){ new (&temp[i]) T(data[i]); } // 移动构造新元素(更高效) new (&temp[size]) T(std::move(item)); // 析构原数组的所有对象 for(int i = 0; i < size; ++i){ data[i].~T(); } // 释放原内存 operator delete(data); // 更新指针和大小 data = temp; size++; return true; }
2. 修正构造函数
template<typename T> DynamicList<T>::DynamicList() : size(0), data(nullptr) {}
3. 修正赋值运算符(拷贝并交换 idiom)
保证异常安全且逻辑正确,同时更新头文件声明:
// 头文件声明改为:DynamicList& operator=(DynamicList obj); template<typename T> DynamicList<T>& DynamicList<T>::operator=(DynamicList<T> obj){ std::swap(size, obj.size); std::swap(data, obj.data); return *this; }
4. 修正operator[]
添加越界检查,返回引用支持修改:
// 头文件添加两个版本的声明 template<typename T> T& DynamicList<T>::operator[](int index){ if(index < 0 || index >= size){ throw std::out_of_range("Index out of bounds"); } return data[index]; } template<typename T> const T& DynamicList<T>::operator[](int index) const { if(index < 0 || index >= size){ throw std::out_of_range("Index out of bounds"); } return data[index]; }
5. 修正析构函数
先析构所有对象再释放内存:
template<typename T> DynamicList<T>::~DynamicList(){ for(int i = 0; i < size; ++i){ data[i].~T(); } operator delete(data); std::cout << "Deleting array of size: " << size << std::endl; }
6. 模板编译注意事项
模板类的实现建议直接放在头文件中,或在头文件末尾包含cpp文件(当前main.cpp的做法可行,但不够规范),确保编译器能实例化模板代码。
内容的提问来源于stack exchange,提问作者John Smith
相关产品推荐
相关产品推荐

