【问题标题】:Deleting a member array of pointers, then reallocating it删除指针的成员数组,然后重新分配它
【发布时间】:2016-10-03 10:34:46
【问题描述】:

我正在创建一个堆栈类作为尝试学习一些 C++ 概念(此处为初始化程序列表、内存管理和模板)的练习。我遇到了一些我无法理解的事情。

在函数 void Stack::push(const T& item) 中,如果我取消注释删除 [] 数据;行,当模板参数为例如 int 或 char 时,我的代码运行良好。但是使用 std::string,我会遇到奇怪的内存错误。

我的想法是我需要一个更大的数组 -> 数组无法调整大小 -> 我创建了一个新数组 -> 我为即将不需要的数组释放了我需要的内存 -> 我制作了现有指针指向我创建更大数组的新内存地址。

现在,当我注释删除行时,即使使用 std::string,代码也运行良好,但我不明白为什么我不能对所有类型安全地执行删除操作。

我们将不胜感激。

#include <iostream>
#include <stdio.h>
#include <memory.h>

template<class T>
class Stack
{
    T* data;
    int sz;

public:
    //Stack(){sz=0;}
    Stack(const std::initializer_list<T>&);
    ~Stack();

    void push(const T&);
    T& pop();

    void show() const;
};

template<class T>
Stack<T>::Stack(const std::initializer_list<T> &list)
{
    sz=0;
    data = new T[list.size()];

    for (auto i : list) {
        data[sz] = i;
        ++sz;
    }
    std::cout<< "Created with sz:  "<< sz<<std::endl;
}

template<class T>
Stack<T>::~Stack()
{
    delete [] data;
}

template<class T> 
void Stack<T>::push(const T& item) {
    std::cout<<"push "<<item<<std::endl;
    T* arr = new T[sz];
    memcpy(arr, data, sz*sizeof(T));
    //delete [] data;
    data = new T[sz + 1];
    memcpy(data, arr, sz*sizeof(T));
    ++sz;
    data[sz - 1] = item;
    std::cout<<"new size: "<<sz<<", bytes: "<<sz*sizeof(T)<<std::endl;
}

template<class T>
T& Stack<T>::pop()
{
    if(sz > 0) {
        std::cout<<"pop "<<data[sz-1]<<std::endl;
        std::cout<<"new size: "<<sz-1<<std::endl;
        return data[--sz];
    }
    else
        return data[0];
}

template<class T>
void Stack<T>::show() const
{
    for (int i=0; i<sz; i++) {
        std::cout<<data[i]<<" ";
    }
    std::cout<<std::endl;
}

int main(){
    Stack<int> s = {1,2,3,4,5,6,7,8,9,10,11};
    s.show();
    s.push(12);
    s.push(13);
    s.push(14);
    s.pop();
    s.pop();
    s.push(15);
    s.push(16);
    s.show();
    Stack<std::string> d = {"one","two","three"};
    d.show();
    d.pop();
    d.push("four");
    d.show();
    return 0;
}

【问题讨论】:

  • 调用delete[] 调用数组中对象的析构函数,但您不希望它们被销毁,因为您只是在移动它们。最后,析构函数被调用了两次,导致错误。
  • 我对 delete [] 的实际作用有一个错误的印象。谢谢你的解释。

标签: c++


【解决方案1】:

不要使用memcpy 复制对象,这样会复制位,但是对于某些对象,按位复制是不正确的,因为不会使用复制构造函数(或复制赋值运算符)。

一个简单的好例子是如果你有一堆std::string 对象。当您进行逐位复制(使用memcpy)时,std::string objects 的内容将被复制,但这基本上只是一个指针和一个大小。当您进行按位复制时,您将有两个 std::string 对象引用相同的内存。销毁其中一个对象将导致另一个对象有一个指向某些内存(用于包含字符串)的杂散指针,该内存不再由您的程序拥有。

要解决这个问题,请使用std::copy 代替复制对象,它会做正确的事情。


与您的问题无关,但您的 push 函数做了一个它不需要的副本:

T* arr = new T[sz];
memcpy(arr, data, sz*sizeof(T));

这根本不需要,而是做类似的事情

T* oldData = data;
data = new T[sz + 1];

// Copy from old array to new
std::copy(oldData, oldData + sz, data);

delete[] oldData;

【讨论】:

  • 感谢您的回答和示例,它确实有很大帮助。
  • 为了澄清以后的读者,上面std::copy的使用有一点错误。正确的格式是 std::copy(oldData, oldData+sz, data);,因为参数是从源开始、源结束、目标开始的迭代器。修复后,使用 std::copy 解决了我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-05
  • 1970-01-01
  • 2018-03-31
  • 2020-07-24
  • 2023-03-18
  • 1970-01-01
  • 2013-09-17
相关资源
最近更新 更多