时间复杂度删除运营商
什么是delete[]
运算符的时间复杂性 ?
我的意思是它是如何实现的 – 是否遍历数组中的所有元素并调用每个元素的析构函数?
这个操作符是否对原始types ( int
等)和用户定义的types做同样的事情?
::operator delete[]
logging在cplusplus.com上 (有时会被忽视 ):
operator delete[]
可以显式调用为常规函数,但在C ++中,delete[]
是具有非常特定行为的运算符:具有delete[]
运算符的expression式首先调用数组中每个元素的适当析构函数如果这些是类types的),然后调用函数operator delete[]
(即这个函数)来释放存储。
所以析构函数被调用n次(每个元素一次),然后释放内存释放“函数”一次。
注意每个销毁可能需要一个不同的时间(甚至是复杂的)。 一般而言,大多数破坏是快速的,并具有相同的复杂性….但是,如果每个被破坏的元素是一个复杂的树或节点或graphics,情况就不会这样…
对于像int
的基本types, int
的虚构析构函数是没有操作的。 编译器可能会优化(如果问)。
你应该检查真正的C ++ 11标准,或者至less是n3337后期的工作草案,在n3337的§5.3.5.6页110中说(感谢Matteo Italia在评论中指出)
如果delete-expression的操作数的值不是空指针值,则delete-expression将调用该对象的析构函数(如果有)或要删除的数组的元素。 在数组的情况下,这些元素将按照地址递减的顺序被销毁(即按照构造函数完成的相反顺序;见12.6.2)
如果你使用了GCC 4.8以上的版本,那么你可以使用g++
编译器和-fdump-tree-phiopt
或者-fdump-tree-all
选项(注意,它们正在倾倒大量的文件!),或者MELT插件来查询某个示例的中间Gimple表示。 或者使用-S -fverbose-asm
来获取汇编代码。 而且您还想要添加优化标志,如-O1
或-O2
…
注意:恕我直言, cppreference.com也是一个有趣的网站关于C + +,看到有关delete
(由Cubbi评论)
delete
和delete[]
的实现由两个阶段组成:
- recursion调用析构函数(如果有的话)
- 内存释放已删除的对象
更不要说一连串的调用析构函数了,它们的复杂性基本上由你来pipe理,我们留下的是如何释放内存来考虑。
第二点不在C ++规范中。 所以,任何编译器套件/操作系统都可以自由采用自己的策略。
一个常见的内存分配/释放策略是在需要的时候从操作系统分配一个完整的内存页面,然后在每个new
/ new[]
返回一个合适大小的块,其长度和属性存储在页面内部作为头/页脚。 相应的delete
/ delete[]
可以像将“chunk”标记为“free”一样简单,这显然是O(1)。
如果内存释放的复杂性是O(1),那么delete
的复杂性实质上就是对析构函数的调用 。 默认实现(几乎)什么都不做,对于单个调用来说是O(1),因此总体上是O(n) ,其中n是调用的总体数量(例如,如果被破坏的对象有两个析构函数为称为,那么n = 1 (object) + 2 (o. fields) = 3
)。
把所有的东西放在一起:你可以通过在析构函数中执行操作来任意增加复杂性(可以由你写),但是你不能比在前一段中定义的O(n)更好地执行。 正式的说法是:“ delete
的复杂性是欧米茄(n)” 。
¹请允许我在这一点上有点不正式
对于类types,理论上的复杂度是O(n)
。 为每个元素调用析构函数。 当然,要坚持可观察的行为,所以如果析构函数是无操作的,或者行为与将整个块标记为释放相同,则复杂度可能只是O(1)
。
对于原始types,编译器可能会立即释放整个内存块,因此复杂度为O(1)
。
什么是
delete[]
运算符的时间复杂性?
所需的时间量当然是实现定义的。 但是,操作符只适用于指向一维数组的指针,因此它是O(1)。
我的意思是它是如何实现的 – 是否遍历数组中的所有元素并调用每个元素的析构函数?
是。
只要它只在指定了使用new[]
创build的内存的精确指针上被调用。 对于原始types,没有用户定义的析构函数。
这个操作符是否对原始types(int等)和用户定义的types做同样的事情?
比较不公平,因为原始types没有用户定义的析构函数,也不能是多态的。
例如,在多态类上delete[]
是一个未定义的行为 。 即
Base* p1 = new Derived, *p2 = new Derived[2]; delete p1; // ok delete[] p2; // bad