在C++向量中手动执行内存重新分配

3
这个链接中可以看到,使用c++向量进行内存重新分配有四个步骤:
  1. 为所需的新容量分配足够的内存;
  2. 将元素从旧内存复制到新内存;
  3. 销毁旧内存中的元素;
  4. 并释放旧内存。
我特别关注第3和第4步,它们是否可以通过代码执行?还是只是在后台发生?
也就是说,我能否通过C++代码“销毁内存中的元素”?我能否直接在C++代码中“释放内存”?

老实说,我没有看完整个链接,但是关于第二点:除非向量末尾之后的空间已经被占用,否则复制并不是一个好策略,那么你就必须执行所有的步骤。如果没有,你只需请求更多的字节并避免第2、3和4点即可。在纯净环境下,可以使用malloc、realloc和free。在C++中使用new 和delete。 - user3477273
4个回答

2
是的,你可以考虑使用 std::allocator 类型或者了解放置 new,operator new,析构函数调用和 operator delete。请注意,operator newoperator delete 是分配和释放内存的函数名称,不同于 new 和 delete 运算符。

希望这能帮到你!


1
我能否通过C++代码“销毁内存中的元素”?
是的。通过调用对象的析构函数。例如,如果x是类型为T的对象的引用,您可以使用以下代码销毁该对象:
x.~T();

当然可以。有多种对应的函数可用于释放内存,如果你使用了malloc进行内存分配,则需要使用free来释放;如果你使用了operator new()进行内存分配,则需要使用operator delete()来释放;如果你使用了new char[]进行内存分配,则需要使用delete[]来释放。

1
@Ogen:对于向量,它调用其分配器的“deallocate”函数。如果你只使用默认的分配器(std::allocator),这意味着它调用operator delete(void*)。 - Benjamin Lindley
@Ogen:等等,你是在问如何模拟向量所做的事情(例如,在你自己的类中实现类似向量的操作)吗?还是在问如何在std::vector上执行这些操作?如果是后者,那么你不应该这样做,因为std::vector会自动完成所有这些操作。 - Benjamin Lindley
@BenjaminLindley,你提到了 vector,它应该调用数组版本的删除对。 - ixSci
1
@ixSci:std::vector 通过其分配器处理其分配和释放。std::vector 的默认分配器是 std::allocatorstd::allocator::deallocate 调用的是 operator delete(void*),而不是 operator delete[](void*) - Benjamin Lindley
@BenjaminLindley 我是在问后者。我知道向量可以自动完成,但我只是想知道它是否可以手动完成。显然它是可以的。 - Ogen
显示剩余5条评论

1
你可以使用简单的STL函数来实现这个。
  • you could use the vector::erase member function to remove a single element or a range of elements from your vector,

    iterator erase (const_iterator position);
    iterator erase (const_iterator first, const_iterator last); 
    
  • and then, under C++11, call vector::shrink_to_fit to request the container to reduce it's capacity to exactly it's current size. (Note that the implementation is free to ignore this request though..)

        void shrink_to_fit();
    

    or use vector::resize

    void resize (size_type n);
    void resize (size_type n, const value_type& val);
    

对于类似这样的内容:

myVector.erase(myVector.begin(), myVector.begin() + 3);
myVector.shrink_to_fit();

或者
myVector.erase(myVector.begin(), myVector.begin() + 3);
resize(myVector.size()); 

如果您将大小调整为小于实际大小的尺寸 - 假设您的向量有10个元素,并且您将其调整为size_t = 7,那么在此之后,您的向量将只有前7个元素,其余元素将被销毁。
另外,如果您只想销毁一些特定的元素,可以使用“删除-移除惯用语” - https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom 然后使用 shrink_to_fit()resize()

0

是的,在C++中,销毁和释放数据既可能也是必须的。

通常情况下,数据可以存在于C++中的两个位置:

  1. 堆栈,由系统自动管理;
  2. 堆,需要你负责删除不再需要的数据。

.

 void fn(void) {
   Data data;
 }

在像上面那样的函数中,一旦调用函数,就会在堆栈中为变量数据保留一些空间。然后它的构造函数将被调用,该函数可以执行其他设置(您可以编写它们并进行任何操作)。在返回函数时,首先调用析构函数(您也可以并且有时必须自己实现),然后通过移动堆栈指针释放堆栈上占用的空间。所有这些都是自动发生的。
void fn2(void) {
  Data * dataptr;
  dataptr = new Data();
  delete dataptr;
}

在这个函数中,与上面相同的事情正在发生,只是使用了一个指针,你可以将其视为描述内存某个位置的整数。(仅在理论上如此,因为像这样的指针没有构造函数或析构函数)

但是接下来还有第二和第三行:在堆中分配和构造了一个Data实例,并在之后进行了析构和释放。但我不必在那里立即执行它。我可以存储指针并在函数返回后长时间使用它来访问实例。

所有这些都包装在类中,例如vector,以提供与托管语言中相同的便利性,只有在需要时才能使用它,而不是被迫使用它。

作为示例,这里是一个玩具向量,仅显示资源管理部分:

struct vec {
  size_t size;
  int * values;
  vec() {
    // constructor, may do anything here
    size = 0;
    values = nullptr;
  }
  free() {
    if (values != nullptr) {
      delete [] values;
    }
  }
  void push_back(int v) {
    // allocating new memory
    int * newvalues = new int[size + 1];
    // copy existing values
    for (size_t it = 0; it < size; ++it) {
      newvalues[it] = values[it];
    }
    // delete original values
    free ();
    // add new value at the now free spot
    newvalues[size] = v;
    ++size;
    // update the pointer to point to the new memory
    values = newvalues;
  }
  ~vec() {
    // destructor, may do anything here
    free();
  }
};

再说一遍:这只是玩具代码,用来说明一般思路,对于真正的代码还有很多要考虑的。

最后注意:在释放内存和销毁对象时,通常是同时进行的。这几乎是你所需要的。但你也可以手动调用析构函数:

Data data;
// ...
data.~Data();

对于分配和构造实例也是如此。有一些内存可以使用放置 new 来调用构造函数。

此外,构造函数和析构函数不仅仅是关于管理内存,它们还可以帮助打开文件、播放音乐或几乎所有需要初始化和完成的事情。

我希望这个快速而粗略的介绍能够帮助您理解基本概念。我省略了很多内容,并且在某些地方不够精确,因此只能将其用作进一步研究的基础。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接