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

从零实现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)
原因分析
  1. 内存分配逻辑完全错误:append中创建temp的代码new T(sizeof(T)*(size+1), alignof(T)),并不是分配能容纳size+1个T对象的内存块,而是调用std::string的构造函数,传入整数参数创建了单个string对象。后续访问temp[i](i≥1)属于越界访问,直接破坏内存结构,最终导致delete[] data时触发段错误。
  2. placement new使用错误:条件size>1导致size=1时不会拷贝已有元素;同时,即使拷贝,也没有正确处理原对象的析构逻辑,且内存越界已经导致内存 corruption。
  3. 其他潜在问题:赋值运算符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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 20:34:54