【问题标题】:How to realloc in c++?如何在 C++ 中重新分配?
【发布时间】:2015-09-23 00:23:09
【问题描述】:

以下代码构成MCVE,这重现了我想问的问题,但它不是真正的代码。真正的代码要复杂得多,所以我写这个是为了演示这个问题。

我正在寻找的关键功能是能够增长动态分配的数组,请不要建议使用stl,因为它被明确禁止。此代码用于教育目的,因此存在限制。

#include <cstring>
#include <iostream>

class Value
{
    public:
        Value(int value = 0);
        Value(const Value &value);
        Value &operator =(const Value &other);
        ~Value();

        operator int() {return *m_absurdPointer;}

    private:
        int *m_absurdPointer;
};

Value::Value(int value) :
    m_absurdPointer(new int[1])
{
    *m_absurdPointer = value;
}

Value::Value(const Value &value)
{
    m_absurdPointer = new int[1];
    memcpy(m_absurdPointer, value.m_absurdPointer, sizeof(*m_absurdPointer));
}

Value &Value::operator =(const Value &other)
{
    m_absurdPointer = new int[1];
    memcpy(m_absurdPointer, other.m_absurdPointer, sizeof(*m_absurdPointer));

    return *this;
}

Value::~Value()
{
    delete[] m_absurdPointer;
}

class ValueArray
{
    public:
        ValueArray();
        ~ValueArray();

        void append(const Value &value);
        void show() const;

    private:
        Value *m_array;
        unsigned int m_capacity;
        unsigned int m_length;
};


ValueArray::ValueArray() :
    m_array(nullptr)
  , m_capacity(0)
  , m_length(0)
{
}

ValueArray::~ValueArray()
{
    delete[] m_array;
}

void
ValueArray::append(const Value &value)
{
    if (m_length >= m_capacity)
    {
        Value *newarray;
        unsigned int unitSize;

        unitSize = 1;       
        newarray = new Value[m_capacity + unitSize];

        if ((m_capacity > 0) && (m_array != nullptr))
            memcpy(newarray, m_array, m_capacity * sizeof(*m_array));
        delete[] m_array;

        m_array = newarray;
        m_capacity += unitSize;
    }
    m_array[m_length++] = value;
}

void
ValueArray::show() const
{
    for (size_t i = 0 ; i < m_length ; ++i)
        std::cout << static_cast<int>(m_array[i]) << std::endl;
}

int
main(void)
{
    ValueArray example;

    for (int i = 0 ; i < 10 ; ++i)
        example.append(Value(i));
    example.show();

    return 0;
}

如您所见,它会导致双重 free 问题,因为 delete[] m_array; 在将值复制到 re-newed 数组后调用了类 Value 的析构函数。

我尝试使用malloc()/realloc() 执行此操作,但我需要调用Value() 的析构函数,因此new 是强制性的,因为我不能使用free()

如何防止这种情况发生?如果我删除delete[] m_absurdPointer;,双重释放当然会消失,但会有内存泄漏。

【问题讨论】:

  • 你可以强制 new 在给定的空间块中分配一些东西,例如new (memblockPtr) SomeType()
  • @iharob,我想说您将 ValueArray 实现为链接列表。您不必使用这种方法重新分配内存,并且您想做的所有其他事情 - 据我所知 - 可能(即使具有相同的运行时限制)
  • @iharob,如果您担心在使用节点时分配过于频繁,请先分配一堆节点并从该池中提取,直到您用完节点等...
  • @WorldSEnder 我想我不能因为我需要在数据上做一个bsearch(),所以我想跳过列表会是一个更好的方法,但我认为这是不允许的.
  • @iharob,如果你需要二分查找,那么二叉树呢?还要解释一下荒谬的指针,因为它有点没用

标签: c++ design-patterns new-operator delete-operator


【解决方案1】:

你基本上是想实现一个自己的向量类,对吧?

好的,首先要做的事情是:据我所知,您无法增加先前分配的内存。至少不使用标准分配器。

所以你需要分配一个新的、更大的内存块。

您可以使用标准方式执行此操作,使用 new:

Type * newdata = new Type[size];

在这种情况下,将为每个新元素调用 Type 类的构造函数,即 size 次。

要将旧数据放入新数组,您需要将其复制或移动到那里:

for (size_t it = 0; it < oldsize; ++it) {
  newdata[it] = olddata[it];
  // newdata[it] = std::move(olddata[it]);
}

这就是std::copy resp。 std::move 正在做。 (你也可以在循环中使用std::swap。)

为此,Type 类需要默认构造函数和复制或移动赋值的有效实现。

您正在使用memcpy。在 C++ 中,这通常是一个坏主意:没有调用您实现的赋值运算符,因此旧数组中的对象和原始副本都使用相同的指针,这就是为什么您可以免费获得双精度。

您还可以分配原始内存并使用placement new 来复制或移动从旧对象构建新对象:

void * memory = new char[size * sizeof(Type)];
for (size_t it = 0; it < oldsize; ++it) {
  new (memory + it * sizeof(Type)) Type(olddata[it]); // copy
}

以上只是一个例子,实际代码也需要考虑对齐。

最后,我确信您可以以某种方式欺骗默认分配器以释放您的(旧)内存而不破坏其中的对象,这允许您使用原始副本 memcpy 制作。虽然这会是一种 hack,并且可能会破坏复杂的类,但这不是 C++ 的做法。

惯用的方法是将旧对象复制或移动到新存储中(使用赋值或构造)。

【讨论】:

    【解决方案2】:

    如果您必须坚持使用ValueArray 的类似矢量的实现,则应该使用移动构造函数:

    class Value
    {
        public:
            Value(int value = 0);
            Value(const Value &value);
            Value(Value&& val);
            Value &operator =(const Value &other);
            Value &operator =(Value&& other);
            ~Value();
    
            operator int() {return *m_absurdPointer;}
    
        private:
            int *m_absurdPointer;
    };
    
    Value::Value(Value&& o) : m_absurdPointer(o.m_absurdPointer) {
      o.m_absurdPointer = nullptr;
    }
    Value &operator =(Value&& o) {
      delete[] this->m_absurdPointer;
      this->m_absurdPointer = o.m_absurdPointer;
      o.m_absurdPointer = nullptr;
    }
    
    void
    ValueArray::append(const Value &value)
    {
    if (m_length >= m_capacity)
    {
        Value *newarray;
        unsigned int unitSize;
    
        unitSize = 1;       
        newarray = new Value[m_capacity + unitSize];
    
        if ((m_capacity > 0) && (m_array != nullptr)) {
          std::move(m_array, m_array + m_length, newarray);
        }
        delete[] m_array;
    
        m_array = newarray;
        m_capacity += unitSize;
    }
    }
    

    【讨论】:

      猜你喜欢
      • 2020-12-17
      • 1970-01-01
      • 2012-06-17
      • 2020-07-07
      • 1970-01-01
      • 1970-01-01
      • 2019-10-23
      • 2013-11-30
      相关资源
      最近更新 更多