struct A
{
vector<int> v;
int a;
A(int _a=0)
{
cout<<"construct!"<<endl;
a=_a;
new (&v) vector<int>(_a,0);
}
~A()
{
cout<<"destroy!"<<endl;
v.~vector<int>();
a=0;
}
} fxxk;
void main()
{
for (int i=0;i<100;i++)
{
fxxk.~A();
cout<<1<<endl;
new (&fxxk) A(100000);
}
}
来自学弟的一个程序,在进行了如下输出之后会 RE。
原因
对象析构时在自己的析构函数执行完毕后会依次调用成员的析构函数。
~A() 里面的 ~vector<int>()
多此一举,导致了 double free
其他
placement new 的正规操作:
- 首先持有可用的内存空间,常用途径有申请字符数组,内存池等。
- placement new 分配对象
T * t = new (ptr) T
- 正常使用对象
t.exec()
- 销毁对象
t.~T()
- 重复循环 2-4,直到结束
- 归还内存空间
代码中 main 函数对 placement new 的使用是没问题的。
update
有人问既然析构会导致 double free,为什么第一次析构没有出现?
这是因为第一次构造的 vector 是平凡的,它的三个指针(start,end,end_cap)都是 nullptr ,不需要析构。
template <class _Tp, class _Allocator>
__vector_base<_Tp, _Allocator>::~__vector_base()
{
if (__begin_ != nullptr)
{
clear();
__alloc_traits::deallocate(__alloc(), __begin_, capacity());
}
}
所以更简单的复现 case 只需要构造一个非平凡的 A,不用进行任何的操作就可以复现 crash
#include <vector>
#include <iostream>
using namespace std;
struct A
{
vector<int> v;
int a;
A(int _a=0)
{
cout<<"construct!"<<endl;
a=_a;
new (&v) vector<int>(_a,0);
}
~A()
{
cout<<"destroy!"<<endl;
v.~vector<int>();
a=0;
}
} fxxk(1);
int main() {
}
/Users/lf/repos/untitled/cmake-build-debug/untitled
construct!
destroy!
untitled(25973,0x11daf5e00) malloc: *** error for object 0x7feb1d6043f0: pointer being freed was not allocated
untitled(25973,0x11daf5e00) malloc: *** set a breakpoint in malloc_error_break to debug
Process finished with exit code 134 (interrupted by signal 6: SIGABRT)