如何删除知道这是一个数组?

好的,我认为我们都同意下面的代码会发生什么变化,这取决于传递的内容,

void deleteForMe(int* pointer) { delete[] pointer; } 

指针可能是各种不同的东西,所以对其执行无条件的delete[]是未定义的。 不过,我们假设我们确实正在传递一个数组指针,

 int main() { int* arr = new int[5]; deleteForMe(arr); return 0; } 

我的问题是,在这种情况下,指针一个数组,谁知道这个? 我的意思是,从语言/编译器的angular度来看,它不知道arr是一个数组指针还是指向一个int的指针。 哎呀,它甚至不知道arr是否是dynamic创build的。 但是,如果我做了下面的事情,

 int main() { int* num = new int(1); deleteForMe(num); return 0; } 

操作系统是足够聪明的,只删除一个整数,而不是去某种types的“杀死狂欢”删除超出该点的其余内存(对比,使用strlen和一个非\0终止的string – 它会保持直到击中0)。

那么谁的工作就是要记住这些东西? 操作系统在后台保存某种types的logging吗? (我的意思是,我意识到我是从这个post开始说发生的事情是不确定的,但事实是,“杀人狂”的情景不会发生,所以在实际的世界里有人记得)。

编译器不知道它是一个数组,它信任程序员。 使用delete []删除指向单个int的指针会导致未定义的行为。 你的第二个main()例子是不安全的,即使它不会立即崩溃。

编译器不得不跟踪需要删除多less对象。 它可能会通过过度分配来存储数组大小。 有关更多详细信息,请参阅C ++ Super FAQ 。

一个问题,迄今为止给出的答案似乎没有解决:如果运行时库(而不是操作系统,真的)可以跟踪数组中的东西的数量,那么为什么我们需要所有的delete[]语法? 为什么不能使用一个delete表单来处理所有删除?

对此的回答可以追溯到C ++的C语言(它不再是真正的努力)。Stroustrup的哲学是,程序员不应该为他们没有使用的任何function付费。 如果他们不使用数组,那么他们不应该为每个分配的内存块承担对象数组的成本。

也就是说,如果你的代码简单的

 Foo* foo = new Foo; 

那么为foo分配的内存空间不应包含任何支持Foo数组所需的额外开销。

由于只有数组分配被设置为携带额外的数组大小信息,因此您需要通知运行时库在删除对象时查找该信息。 这就是我们需要使用的原因

 delete[] bar; 

而不是仅仅

 delete bar; 

如果bar是一个指向数组的指针。

对于我们中的大多数人(包括我自己)来说,这些额外的内存字节似乎很古怪。 但是,仍然有一些情况下,保存几个字节(从可能是非常多的内存块)可能是重要的。

是的,操作系统保留了一些东西在后台。 例如,如果你运行

 int* num = new int[5]; 

操作系统可以分配4个额外的字节,将分配的大小存储在分配的内存的前4个字节中,并返回一个偏移量指针(即分配内存空间1000到1024,但指针返回到1004, 1003存储分配的大小)。 然后,当调用delete时,它可以在指针传递给它之前查看4个字节来查找分配的大小。

我相信还有其他的方式来跟踪分配的大小,但这是一个select。

这与这个问题非常相似,它有许多你正在寻找的细节。

但足以说,跟踪任何这些不是OS的工作。 它实际上是运行时库或底层内存pipe理器,它将跟踪数组的大小。 这通常是通过预先分配额外的内存并在该位置存储数组的大小(大部分使用头节点)来完成的。

在一些实现中,通过执行以下代码可以看到这一点

 int* pArray = new int[5]; int size = *(pArray-1); 

deletedelete[]可能会释放分配的内存(内存指向),但最大的区别是数组上的delete不会调用数组的每个元素的析构函数。

无论如何,混合new/new[]delete/delete[]可能是UB。

它不知道它是一个数组,这就是为什么你必须提供delete[]而不是常规的旧delete

我也有类似的问题。 在C中,你用malloc()(或其他类似的函数)分配内存,并用free()将其删除。 只有一个malloc(),它只是分配一定数量的字节。 只有一个空闲(),它只是一个指针,因为它的参数。

那么为什么在C中你可以把指针交给free,但是在C ++中你必须告诉它它是一个数组还是一个variables?

我学到的答案与类的析构函数有关。

如果你分配一个类MyClass的实例…

 classes = new MyClass[3]; 

并用delete删除它,你可能只会得到调用MyClass的第一个实例的析构函数。 如果使用delete [],则可以确保将为数组中的所有实例调用析构函数。

这是重要的区别。 如果你只是使用标准types(例如int),你不会真的看到这个问题。 另外,你应该记住,在new上使用delete和在new上使用delete []的行为是未定义的 – 在每个编译器/系统上它可能不会以相同的方式工作。

这取决于负责内存分配的运行时间,就像您可以使用免费的标准C中删除使用malloc创build的数组一样。 我认为每个编译器实现它不同。 一种常见的方法是为数组大小分配一个额外的单元格。

然而,运行时并不够聪明来检测它是否是一个数组或指针,你必须通知它,如果你错了,你要么不正确地删除(例如,ptr而不是数组),或者你最终将采取与尺寸无关的价值并造成重大损失。

编译器的一种方法是分配更多的内存,并在head元素中存储元素的数量。

示例如何完成:在这里

 int* i = new int[4]; 

编译器将分配sizeof(int)* 5个字节。

 int *temp = malloc(sizeof(int)*5) 

将存储4在第一个sizeof(int)字节

 *temp = 4; 

并设置i

 i = temp + 1; 

所以i指的是4个元素的数组,而不是5个。

 delete[] i; 

将按以下方式处理

 int *temp = i - 1; int numbers_of_element = *temp; // = 4 ... call destructor for numbers_of_element elements if needed ... that are stored in temp + 1, temp + 2, ... temp + 4 free (temp) 

同意编译器不知道它是否是一个数组。 这取决于程序员。

编译器有时会logging多less对象需要通过过度分配来存储数组大小,但并不总是必要的。

有关分配额外存储空间的完整规范,请参阅C ++ ABI(编译器如何实现): Itanium C ++ ABI:Array Operator new Cookies

在语义上,C ++中的两个版本的delete操作符都可以“吃”任何指针; 然而,如果一个指向单个对象的指针被赋予delete[] ,那么UB将会产生,这意味着任何事情都可能发生,包括系统崩溃或什么都没有。

C ++要求程序员根据解除分配的主题来select正确版本的删除操作符:数组或单个对象。

如果编译器可以自动确定传递给delete操作符的指针是否是一个指针数组,那么在C ++中只有一个删除操作符,这对于这两种情况都是足够的。

您不能对数组使用delete ,并且不能将delete []用于非数组。

嘿嘿,这取决于你分配新的[]expression式分配的types或类/结构的构build数组,而你不提供你的构造函数和析构函数将操作符将它视为一个大小“sizeof(object)* numObjects“而不是对象数组,因此在这种情况下,分配的对象数量将不会被存储在任何地方,但是如果您分配对象数组并且在对象中提供了构造器和析构器而不是行为改变,则新expression式将多分配4个字节,对象的前4个字节,因此可以调用每个析构函数,因此new []expression式将返回指向前移4个字节的指针,比返回内存时delete []expression式将首先调用函数模板迭代通过数组对象和调用析构函数为每一个。 我已经创build了这个简单的代码重载new []和delete []expression式,并提供了一个模板函数来释放内存,并在需要时调用每个对象的析构函数:

 // overloaded new expression void* operator new[]( size_t size ) { // allocate 4 bytes more see comment below int* ptr = (int*)malloc( size + 4 ); // set value stored at address to 0 // and shift pointer by 4 bytes to avoid situation that // might arise where two memory blocks // are adjacent and non-zero *ptr = 0; ++ptr; return ptr; } ////////////////////////////////////////// // overloaded delete expression void static operator delete[]( void* ptr ) { // decrement value of pointer to get the // "Real Pointer Value" int* realPtr = (int*)ptr; --realPtr; free( realPtr ); } ////////////////////////////////////////// // Template used to call destructor if needed // and call appropriate delete template<class T> void Deallocate( T* ptr ) { int* instanceCount = (int*)ptr; --instanceCount; if(*instanceCount > 0) // if larger than 0 array is being deleted { // call destructor for each object for(int i = 0; i < *instanceCount; i++) { ptr[i].~T(); } // call delete passing instance count witch points // to begin of array memory ::operator delete[]( instanceCount ); } else { // single instance deleted call destructor // and delete passing ptr ptr->~T(); ::operator delete[]( ptr ); } } // Replace calls to new and delete #define MyNew ::new #define MyDelete(ptr) Deallocate(ptr) // structure with constructor/ destructor struct StructureOne { StructureOne(): someInt(0) {} ~StructureOne() { someInt = 0; } int someInt; }; ////////////////////////////// // structure without constructor/ destructor struct StructureTwo { int someInt; }; ////////////////////////////// void main(void) { const unsigned int numElements = 30; StructureOne* structOne = nullptr; StructureTwo* structTwo = nullptr; int* basicType = nullptr; size_t ArraySize = 0; /**********************************************************************/ // basic type array // place break point here and in new expression // check size and compare it with size passed // in to new expression size will be the same ArraySize = sizeof( int ) * numElements; // this will be treated as size rather than object array as there is no // constructor and destructor. value assigned to basicType pointer // will be the same as value of "++ptr" in new expression basicType = MyNew int[numElements]; // Place break point in template function to see the behavior // destructors will not be called and it will be treated as // single instance of size equal to "sizeof( int ) * numElements" MyDelete( basicType ); /**********************************************************************/ // structure without constructor and destructor array // behavior will be the same as with basic type // place break point here and in new expression // check size and compare it with size passed // in to new expression size will be the same ArraySize = sizeof( StructureTwo ) * numElements; // this will be treated as size rather than object array as there is no // constructor and destructor value assigned to structTwo pointer // will be the same as value of "++ptr" in new expression structTwo = MyNew StructureTwo[numElements]; // Place break point in template function to see the behavior // destructors will not be called and it will be treated as // single instance of size equal to "sizeof( StructureTwo ) * numElements" MyDelete( structTwo ); /**********************************************************************/ // structure with constructor and destructor array // place break point check size and compare it with size passed in // new expression size in expression will be larger by 4 bytes ArraySize = sizeof( StructureOne ) * numElements; // value assigned to "structOne pointer" will be different // of "++ptr" in new expression "shifted by another 4 bytes" structOne = MyNew StructureOne[numElements]; // Place break point in template function to see the behavior // destructors will be called for each array object MyDelete( structOne ); } /////////////////////////////////////////// 

只需在类中定义一个析构函数,然后用这两种语法来执行你的代码

 delete pointer delete [] pointer 

根据输出你可以find解决scheme

答案:

int * pArray = new int [5];

int size = *(pArray-1);

在上面发布是不正确的,并产生无效的价值。 “-1”计数元素在64位Windows操作系统上,正确的缓冲区大小位于Ptr-4字节的地址中