关于new[]与placement new operator的问题.

时间:2021-12-14 09:23:33
class A
{
    int i;
};

int main()
{
    char *pc = new char[sizeof(A)*10];  //1
   
    A *pa = new (pc) A[10];    //2
    A *pa2 = new A[10];  //3
   
    delete [] pc;
    delete [] pa2;
}
1处只是获得10个A大小的空间,而3处虽然大小也是10个A,但是它还需要一个WORD来存储NEW来的大小吧?
以便后面的delete [] pa2正确释放内存.那2处需不需要这个WORD来存储分配的大小呢?不需要是吧.因为它是placement new,因为它不需要管理内存的释放,但如果我后面是delete [] pa;来释放pc指向的内存,我是说如果,那它怎么知道该释放多少个大小的A所占的内存?也就是讲2与3得来的空间大小都不一样是吧?

20 个解决方案

#1


参与

#2


第二种是什么,楼主在哪里看到的?什么意思啊,弱问ing!

#3


delete [] pa不合适吧,呵呵。

#4


pc是个类型吗?编译器能通过么?

#5


都未说到点子上.希望有高人指点.

#6


delete [] pa很可能会导致程序崩溃,不信你可以试试

#7


3处好像不需要一个WORD来存储NEW来的大小吧
delete[]的原型是这样的
void operator delete[](void*,size_t)
调用delete[]时,编译器会用数组的大小给第二个参数的

delete [] pc;前,应该调用一下类A的析构函数(当然,这里没有必要),因为placement new出来的内存delete时,不会自动调用类的析构函数的。

#8


调用delete[]时编译器怎么知道数组的大小呢?
你这个void operator delete[](void*,size_t)是哪里看来的?我的gcc,icc上都没有这样的原型.


但如果我后面是delete [] pa;来释放pc指向的内存,我是说如果,那它怎么知道该释放多少个大小的A所占的内存?

#9


1处只是获得10个A大小的空间,而3处虽然大小也是10个A,但是它还需要一个WORD来存储NEW来的大小吧?
---------------------------------------------------------------------------------
我觉得是否用word来存储new出来的大小是编译器内部的实现,可能用WORD。所以实际分配的内存可能是:new的内存 + 4(WORD)(也许其他的值)。


---------------------------------------------------------------------------------
那2处需不需要这个WORD来存储分配的大小呢?不需要是吧.因为它是placement new,因为它不需要管理内存的释放,但如果我后面是delete [] pa;来释放pc指向的内存,我是说如果,那它怎么知道该释放多少个大小的A所占的内存?也就是讲2与3得来的空间大小都不一样是吧?
---------------------------------------------------------------------------------
2处不需要,因为placement new直接返回参数的指针,其实参数指针的地址已经包含分配额外的内存。

如果用delete [] pa释放pc指向的内存时:如果明确提供A的析构函数~A(),在调用~A()的时候,将发生不可预知的错误。因为A* p = new A()的内存肯定和通过placement new的内存不一样,所以delete的时候,在debug中类型检查错误,抛出异常;在release中则发生不可预知的错误(可能根据编译器的情况而不同,vc7.0如上)。也就是A析构了其他类型的对象地址,当然出错。

2的大小和1的一样,这里应该和3一样。



#10


其实着题的本意是(placement expression)

new 它是机器分配的,你无法自己决定分配的块;
sample:

class Myclass{};

通过分配函数,在使用new时,我们可以将对象(你创建的类对象,结构,文件,流)
放到任何地方。

void* operator new (unsigned int , void* p) {return p;}
void* buf = reinterpret_cast<void*>(0xFFEE);//你确定没问题的地址
Myclass* p2 = new(buf) Myclass; //X

但 throw (std::bad_alloc),这能否释放分配的store。
应该显式的调用 delete,而不要期望std::bad_alloc能帮你解决问题。

#11


我比较喜欢中文ID :#

#12


哦,忘了说明: 我只是来灌水的 :@

#13


new分配的内存大小是保存在分配空间之前的4个字节上的。

但对于new placement就不是很了解。
个人认为new placement分配了的内存是保存了的。
做个试验
class A
{
public:
    int i;
};
(1)当空间不够时
int main()
{
    char *pc = new char[1];  //1
   
    A *pa = new (pc) A;    //2
    pa->i=10;
    delete [] pc;
}

(2)空间足够
int main()
{
    char *pc = new char[sizeof(A)];  //1
   
    A *pa = new (pc) A;    //2
    pa->i=10;
    delete [] pc;
}
(1)在vc6下要处错,在gcc下不报错。
(2)都不报错.

按道理上讲,new placement并不分配内存,仅仅在pc处调用ctor,如果不够的话,它是会剪裁的。那么就不会出现错误的(VC6)。

是不是编译器的问题?!

#14


个人认为new placement分配了的内存是保存了的。

/////////////////////////////////
这句话说错了。应该是new placement使用的内存是没有保存的。而不是分配的内存。

#15


to:xiao_wang(小王)
2处不需要,因为placement new直接返回参数的指针,其实参数指针的地址已经包含分配额外的内存。
//参数指针的地址已经包含分配额外的内存这句话能不能具体的说明一下?
//如何证明它是对的?它如何包含的?你额外的内存指的是表示分配的大小的吧?

2的大小和1的一样,这里应该和3一样。
//照你说如果参数指针包含这个额外的内存的话,那就是1和3一样,它们都需要一个word
//而2不一样,它在参数指针中就包含了这个信息.但不知道你说的如何考证.

to qwertasdfg123(流星):
我也认为placement new不需要保存这个大小的信息.因为它根本不负责释放.
它只是调用ctor,所以一切用delete来释放由placement new得来的内存都是未定义的形为.
所以结论就是我的问题没有意义.:)

#16


对,不过用这个的时候注意边界对齐是好习惯

#17


一般来说placement new 的实现是这样的

void* new(size_t size, void* where)
{
    return where;
}

一般这样来应用,就是给已经分配的内存,调用类的构造函数初始化
char* pc = new char[10];
A* pa = new(pc) A();

我的意思是说,pc是new出来的,那么给pc分配的内存一定包含了这块内存的小大,有可能是一个word来存放着个大小。由于placement new 直接返回地址,所以pa的地址应该和pc一样。这里我猜测参数pc包含了额外word,如果没有,那么pa也会在new operator 后重写原来额外word的内存。

#18


pa和pc的地址应该是一样的。
不知道 额外word 指的什么?

#19


placement new 根本没有进行什么“内存操作”,直接返回调用构造函数,所以在考虑内存分配释放的时候完全可以忽略它的存在。

char *pc = new char[sizeof(A)*10]; //1
A *pa = new (pc) A[10]; //2

delete [] pc;
delete [] pa2;

你认为这样两组分配和释放,有什么区别吗?

#20


抱歉,我是说

char *pc = new char[sizeof(A)*10]; //1
A *pa2 = new A[10]; //3

delete [] pc;
delete [] pa2;

这两组:)

#1


参与

#2


第二种是什么,楼主在哪里看到的?什么意思啊,弱问ing!

#3


delete [] pa不合适吧,呵呵。

#4


pc是个类型吗?编译器能通过么?

#5


都未说到点子上.希望有高人指点.

#6


delete [] pa很可能会导致程序崩溃,不信你可以试试

#7


3处好像不需要一个WORD来存储NEW来的大小吧
delete[]的原型是这样的
void operator delete[](void*,size_t)
调用delete[]时,编译器会用数组的大小给第二个参数的

delete [] pc;前,应该调用一下类A的析构函数(当然,这里没有必要),因为placement new出来的内存delete时,不会自动调用类的析构函数的。

#8


调用delete[]时编译器怎么知道数组的大小呢?
你这个void operator delete[](void*,size_t)是哪里看来的?我的gcc,icc上都没有这样的原型.


但如果我后面是delete [] pa;来释放pc指向的内存,我是说如果,那它怎么知道该释放多少个大小的A所占的内存?

#9


1处只是获得10个A大小的空间,而3处虽然大小也是10个A,但是它还需要一个WORD来存储NEW来的大小吧?
---------------------------------------------------------------------------------
我觉得是否用word来存储new出来的大小是编译器内部的实现,可能用WORD。所以实际分配的内存可能是:new的内存 + 4(WORD)(也许其他的值)。


---------------------------------------------------------------------------------
那2处需不需要这个WORD来存储分配的大小呢?不需要是吧.因为它是placement new,因为它不需要管理内存的释放,但如果我后面是delete [] pa;来释放pc指向的内存,我是说如果,那它怎么知道该释放多少个大小的A所占的内存?也就是讲2与3得来的空间大小都不一样是吧?
---------------------------------------------------------------------------------
2处不需要,因为placement new直接返回参数的指针,其实参数指针的地址已经包含分配额外的内存。

如果用delete [] pa释放pc指向的内存时:如果明确提供A的析构函数~A(),在调用~A()的时候,将发生不可预知的错误。因为A* p = new A()的内存肯定和通过placement new的内存不一样,所以delete的时候,在debug中类型检查错误,抛出异常;在release中则发生不可预知的错误(可能根据编译器的情况而不同,vc7.0如上)。也就是A析构了其他类型的对象地址,当然出错。

2的大小和1的一样,这里应该和3一样。



#10


其实着题的本意是(placement expression)

new 它是机器分配的,你无法自己决定分配的块;
sample:

class Myclass{};

通过分配函数,在使用new时,我们可以将对象(你创建的类对象,结构,文件,流)
放到任何地方。

void* operator new (unsigned int , void* p) {return p;}
void* buf = reinterpret_cast<void*>(0xFFEE);//你确定没问题的地址
Myclass* p2 = new(buf) Myclass; //X

但 throw (std::bad_alloc),这能否释放分配的store。
应该显式的调用 delete,而不要期望std::bad_alloc能帮你解决问题。

#11


我比较喜欢中文ID :#

#12


哦,忘了说明: 我只是来灌水的 :@

#13


new分配的内存大小是保存在分配空间之前的4个字节上的。

但对于new placement就不是很了解。
个人认为new placement分配了的内存是保存了的。
做个试验
class A
{
public:
    int i;
};
(1)当空间不够时
int main()
{
    char *pc = new char[1];  //1
   
    A *pa = new (pc) A;    //2
    pa->i=10;
    delete [] pc;
}

(2)空间足够
int main()
{
    char *pc = new char[sizeof(A)];  //1
   
    A *pa = new (pc) A;    //2
    pa->i=10;
    delete [] pc;
}
(1)在vc6下要处错,在gcc下不报错。
(2)都不报错.

按道理上讲,new placement并不分配内存,仅仅在pc处调用ctor,如果不够的话,它是会剪裁的。那么就不会出现错误的(VC6)。

是不是编译器的问题?!

#14


个人认为new placement分配了的内存是保存了的。

/////////////////////////////////
这句话说错了。应该是new placement使用的内存是没有保存的。而不是分配的内存。

#15


to:xiao_wang(小王)
2处不需要,因为placement new直接返回参数的指针,其实参数指针的地址已经包含分配额外的内存。
//参数指针的地址已经包含分配额外的内存这句话能不能具体的说明一下?
//如何证明它是对的?它如何包含的?你额外的内存指的是表示分配的大小的吧?

2的大小和1的一样,这里应该和3一样。
//照你说如果参数指针包含这个额外的内存的话,那就是1和3一样,它们都需要一个word
//而2不一样,它在参数指针中就包含了这个信息.但不知道你说的如何考证.

to qwertasdfg123(流星):
我也认为placement new不需要保存这个大小的信息.因为它根本不负责释放.
它只是调用ctor,所以一切用delete来释放由placement new得来的内存都是未定义的形为.
所以结论就是我的问题没有意义.:)

#16


对,不过用这个的时候注意边界对齐是好习惯

#17


一般来说placement new 的实现是这样的

void* new(size_t size, void* where)
{
    return where;
}

一般这样来应用,就是给已经分配的内存,调用类的构造函数初始化
char* pc = new char[10];
A* pa = new(pc) A();

我的意思是说,pc是new出来的,那么给pc分配的内存一定包含了这块内存的小大,有可能是一个word来存放着个大小。由于placement new 直接返回地址,所以pa的地址应该和pc一样。这里我猜测参数pc包含了额外word,如果没有,那么pa也会在new operator 后重写原来额外word的内存。

#18


pa和pc的地址应该是一样的。
不知道 额外word 指的什么?

#19


placement new 根本没有进行什么“内存操作”,直接返回调用构造函数,所以在考虑内存分配释放的时候完全可以忽略它的存在。

char *pc = new char[sizeof(A)*10]; //1
A *pa = new (pc) A[10]; //2

delete [] pc;
delete [] pa2;

你认为这样两组分配和释放,有什么区别吗?

#20


抱歉,我是说

char *pc = new char[sizeof(A)*10]; //1
A *pa2 = new A[10]; //3

delete [] pc;
delete [] pa2;

这两组:)

#21