C++语句的 Big-O 是 O(1) 还是 O(n)'delete [] Q;'?

Is Big-O of the C++ statement 'delete [] Q;' O(1) or O(n)?

本文关键字:delete 还是 语句 Big-O C++      更新时间:2023-10-16

标题不言自明。很简单的问题。我想是0 (n),但我想在明天期末考试前验证一下。

简短的回答是…这取决于.

如果Q是指向具有析构函数的对象数组的指针,则delete[] Q需要调用所有这些析构函数。这将调用O(n)个析构函数,其中n是数组中元素的数量。另一方面,如果Q指向一个没有析构函数的对象数组(例如,int s或简单的struct s),则不需要调用析构函数,这只需要O(1)时间。

现在请注意,这些析构函数不必每个都在O(1)时间内运行。如果对象是std::vector对象,那么每个析构函数依次必须触发更多的释放。在不进一步了解这些对象的情况下,我们只能说,如果调用了析构函数,如果析构函数是普通的,则将调用0个析构函数,否则将调用O(n)个析构函数。

但是这忽略了堆分配器如何工作的实现细节。释放一块内存可能需要O(log K)时间,其中K是分配的块的总数,或者可能需要O(1)时间,不管有多少块内存,或者可能需要O(log log K),等等。在不知道分配器是如何工作的情况下,你真的不能确定。

简而言之,如果您只关注在将块交还给内存分配器之前清理对象所需的工作,则如果存储的对象具有析构函数,则调用O(n)个析构函数,否则调用0个析构函数。这些析构函数可能需要相当长的时间才能完成。然后,有将内存块重新引入内存分配器的成本,这可能会占用它自己的时间。

希望这对你有帮助!

我同意这要视情况而定,但是让我们只讨论释放X字节的内存,而不用担心析构函数。

一些内存分配器为"小"(1到500字节)对象保留空闲列表。对列表的插入是O(1)。如果所有线程都有一个空闲列表,那么它需要获取一个互斥锁。获取互斥锁通常涉及到多达1000个"旋转",然后可能是一个系统调用(这是非常昂贵的)。有些分配器使用线程本地存储为每个线程提供空闲列表,因此它们不需要获取互斥锁。

对于中等大小(500到60000字节)的对象,许多分配器将执行块合并。也就是说,它们检查相邻的块是否也是空闲的,然后将2或3个空闲块合并成一个更大的空闲块。合并是0(1),但是它同样需要获取一个互斥锁。

对于大块,一些分配器使用系统调用获得内存。所以释放内存也是一个系统调用。