![fb2b71479047be09187365586eee3c9f.png](https://i-blog.csdnimg.cn/blog_migrate/eb55abb127449494895350c537ea939a.jpeg)
new delete
new:
方式一:string *p = new string("123")
;
- - 编译器插入一行代码,调用operator-new函数,分配原始的,未命名的内存空间以便存储,
- - 执行构造函数。返回指向该对象的指针
方式二:string *p = new (pold) string("123");
- - 一个特殊的标准库函数
operator new(size_t, void*)被调用
,它什么也不做。 - - 在pold所指地址上构造对象
new是一个操作符,无法被重载,它总是执行两步:
- - 调用某个operator-new函数
- - 构造对象
只是当它遇到方式二时,调用的是一个特殊的operator-new函数;从而跳过分配空间这一步。这种时候,new被称为placement new。
注:operator new(size_t, void*)
无法被重载,否则placement new可能执行失败。
再次强调,new delete是操作符,placement new是指new以方式二的情况被调用。operator-new和operator-delete是函数。
当A *pa = new A(-1)
的时候发生了什么
class A {
public:
A(int ia):ia(ia){};
int ia;
};
int main(int argc, char **argv)
{
A *ps = new A(-1) ;
}
翻译成汇编:
0x0000000000401586 mov $0x4,%ecx //sizeof(A) is 4
0x000000000040158b callq 0x4b2980 <_Znwy> // _Znwy is operator new(unsigned long long, call ::opertor new(sizeof(A))
0x0000000000401590 mov %rax,%rbx //为A分配的地址
0x0000000000401593 mov $0xffffffff,%edx //-1
0x0000000000401598 mov %rbx,%rcx
0x000000000040159b callq 0x4210c0 <A::A(int)> //A::A(-1)
0x00000000004015a0 mov %rbx,-0x58(%rbp) // ps = 为A分配的地址
当 new [] 和 delete []的时候发生了什么
当发生 A* pa = new A[num];
的时候:
- - 计算这个数组所需要的size,记为sz(这个size除了放得下数组,应该还放的下num)
- - 调用
operator new[](unsigned long long)
。记::operator new [](sz)
的返回值为p_new - *p_new = num;记录数组的长度,用于delete - - 令p_temp = p_new;用于初始化数组。p_temp += 0x8;这个长度用于存储数组长度。
- - 循环在p_temp上调用构造函数
- - pa = p_new + 8,即指向第一个A对象;
与effective c++中描写的一致:
![03890f54d3b724d58b80040d90aaf8e6.png](https://i-blog.csdnimg.cn/blog_migrate/a9195ef896ec65f0e62a6acc2e56ff7e.png)
当发生delete [] pa
时:
- - num = *(pa - 0x8) - p_temp = pa + offset;其中offset时编译器考虑对齐计算的一个值,使p_temp指向最后一个对象
- - 循环在p_temp上调用析构函数
- - 最后调用
operator delete[](pa)
释放内存。
例子
int main(int argc, char **argv)
{
string *pa = new string[5];
delete [] pa;
}
对应的new和delete代码为:
// 0xa8 = sizeof(array) + 放num的大小
0x000000000040157c mov $0xa8,%ecx
//operator new
0x0000000000401581 callq 0x4b2980 <_Znay>
//*p_new = 0x5
0x0000000000401586 mov %rax,%rbx
0x0000000000401589 movq $0x5,(%rbx)
// p_temp = p_new + 5
0x0000000000401590 lea 0x8(%rbx),%rax
//这部分是循环调用构造函数
0x0000000000401594 mov $0x4,%esi
0x0000000000401599 mov %rax,%rdi
0x000000000040159c test %rsi,%rsi
0x000000000040159f js 0x4015b3 <main(int, char**)+83>
0x00000000004015a1 mov %rdi,%rcx
0x00000000004015a4 callq 0x4a16a0 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1Ev>
0x00000000004015a9 add $0x20,%rdi
0x00000000004015ad sub $0x1,%rsi
0x00000000004015b1 jmp 0x40159c <main(int, char**)+60>
//pa = p_new + 8;
0x00000000004015b3 lea 0x8(%rbx),%rax
0x00000000004015b7 mov %rax,-0x58(%rbp)
//delete [] pa
0x00000000004015bb cmpq $0x0,-0x58(%rbp)
0x00000000004015c0 je 0x401600 <main(int, char**)+160>
0x00000000004015c2 mov -0x58(%rbp),%rax
// num = *(pa - 0x8)
0x00000000004015c6 sub $0x8,%rax
0x00000000004015ca mov (%rax),%rax
//p_temp = &pa[-1]
//然后循环析构
0x00000000004015cd shl $0x5,%rax
0x00000000004015d1 mov %rax,%rdx
0x00000000004015d4 mov -0x58(%rbp),%rax
0x00000000004015d8 lea (%rdx,%rax,1),%rbx
0x00000000004015dc cmp -0x58(%rbp),%rbx
0x00000000004015e0 je 0x4015f0 <main(int, char**)+144>
0x00000000004015e2 sub $0x20,%rbx
0x00000000004015e6 mov %rbx,%rcx
0x00000000004015e9 callq 0x4a19f0 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev>
0x00000000004015ee jmp 0x4015dc <main(int, char**)+124>
0x00000000004015f0 mov -0x58(%rbp),%rax
//::operator delete [] ( pa )
0x00000000004015f4 sub $0x8,%rax
0x00000000004015f8 mov %rax,%rcx
0x00000000004015fb callq 0x4b2950 <_ZdaPv>
delete:
- 执行析构函数
- 调用operator-delete函数释放空间
其中operator-new和operator-delete可以被重载:
- - 作为类的方法被重载
- - 作为一个全局函数被重载
当编译器没有找到用户重载的operator-new和operator-delete时,调用标准库的::operator new和::operator delete