new (&ss1,10)S(); 与 new (&ss1,10)S 有什么不同?

时间:2022-12-09 21:43:21
#include<iostream>
using namespace std;

struct S
{
void* operator new(size_t size, void* p, int i)
{
((S*)p)->i = i;
return p;
}
int i;
};

int main()
{
S ss1;
S *ps1 = new (&ss1,10)S();
cout << ss1.i <<":"<< ps1->i << endl;

S ss2;
S *ps2 = new (&ss2,20)S;
cout << ss2.i <<":"<< ps2->i << endl;

system("pause");
return 0;
}


输出:
0:0
20:20
疑问:为什么第一个输出为0:0,而不是10:10?
      new (&ss1,10)S();  与 new (&ss1,10)S  有什么不同?

17 个解决方案

#1


还真不一样
S *ps1 = new (&ss1,10)S();
会调用memset
而S *ps2 = new (&ss2,20)S;
不会

#2


引用 1 楼 beginnow 的回复:
还真不一样
S *ps1 = new (&ss1,10)S();
会调用memset
而S *ps2 = new (&ss2,20)S;
不会

我不认为是调用了memset,我认为应该是调用了其默认构造函数所致!
比如:
#include<iostream>
using namespace std;

struct S
{
    S() : i(8) {}
    void* operator new(size_t, void* p, int i)
    {
        ((S*)p)->i = i;
        return p;
    }
    int i;
};

int main()
{
    S ss1;
    S *ps1 = new (&ss1, 10) S();
    cout << ss1.i << ":" << ps1->i << endl;

    S ss2;
    S *ps2 = new (&ss2, 20) S;
    cout << ss2.i << ":" << ps2->i << endl;

    system("pause");
    return 0;
}

输出:
8:8
8:8

#3


引用楼主 yingxunren 的回复:
C/C++ code#include<iostream>usingnamespace std;struct S
{void*operatornew(size_t size,void* p,int i)
    {
        ((S*)p)->i= i;return p;
    }int i;
};int main()
{
    S ss1;
    S*ps1=new (&ss1,10)S();
    cout<< ss1.i<<":"<< ps1->i<< endl;

    S ss2;
    S*ps2=new (&ss2,20)S;
    cout<< ss2.i<<":"<< ps2->i<< endl;

    system("pause");return0;
}

 输出:
 0:0
 20:20
 疑问:为什么第一个输出为0:0,而不是10:10?
       new (&ss1,10)S();  与 new (&ss1,10)S  有什么不同?

那为什么第二个输出时10:10呢?

#4


我觉得这个问题得看new的实现。
你重载了new,而new最终会调用S实例的构造函数。
然后才返回指针。
按常理,应该还有个malloc调用,以调用相应内存中的构造函数。
可是,你没有malloc申请内存。
而是代之以一个已经存在的实例。
这会出现 重复调用构造函数

这种行为会导致不确定性的,楼主你肯定也解释不了我在2楼给的代码。

#5


即使你不加static,重载的new也是static的;(你这种用法类似于replacement new)
重载的new只负责分配内存,在内存被分配后,编译器会产生调用构造函数的代码,
加括号会导致内建类型被初始化为默认值( int 就会被初始化为0 )

#6


果然如楼上所言,而不是不确定性的缘故。如果都加括号,则输出都为0

#7


加括号应该是调用了默认构造函数。

#8


我的理解:方式1强迫系统用不带参数的构造函数来初始化对象。由于你没有提供构造函数,因此系统给你一个缺省的,而10由于构造函数不带参数被直接忽略了。

对于方式2,由于结构有一种逐字段初始化的功能,因此20被赋值给第一个成员变量了

个人认为这种带参数placement new的方法非常不好,可读性太差,楼主谨慎使用,否则一旦有问题就象现在这样只好问人。而和你合作的人,很大程度上还要不停来问你这是啥意思。

#9


不管有没有加括号,都会调用不带参数的构造函数。

#10


引用 7 楼 cattycat 的回复:
加括号应该是调用了默认构造函数。

默认构造函数会给i赋一个不确定的值,而不是0.

#11


楼主这种new合法么?真没见过带额外参数得。

#12


void* operator new(size_t size, void* p, int i)
    {
        ((S*)p)->i = i;
        return p;
    }

确实没什么意义,((S*)p)->i = i;改变后,编译器又会调用默认构造函数重新改变i的值。

#13


本帖最后由 Loaden 于 2009-12-06 23:46:31 编辑
带额外参数是可以的,只是,正常情况下如 4# 所说,会malloc内存,比如:

    static void* operator new(size_t size, int type, const char* file, int line)
    {
        void* p = _malloc_dbg(size, type, file, line);
        if (p == NULL) { qpDbgError(); return NULL; }
        HeapMemory::GetInstance().Add(p);
        return p;
    }

而楼主没有malloc,而是用一个已经构建的实例指针返回。
这会让new在实现时, 重复调用该实例的构造函数。
所以,我认为会有不确定性。

#14


大家看写反汇编的代码就知道了:
S ss;
S *pS = new (&ss,10) S();
004124DE  mov         dword ptr [ebp-110h],4 
004124E8  push        0Ah  
004124EA  lea         eax,[ss] 
004124ED  push        eax  
004124EE  mov         ecx,dword ptr [ebp-110h] 
004124F4  push        ecx  
004124F5  call        S::operator new (41121Ch) 
004124FA  add         esp,0Ch 
004124FD  mov         dword ptr [ebp-104h],eax 
00412503  cmp         dword ptr [ebp-104h],0 
0041250A  je          main+72h (412532h) 
0041250C  mov         edx,dword ptr [ebp-110h] 
00412512  push        edx  
00412513  push        0    
00412515  mov         eax,dword ptr [ebp-104h] 
0041251B  push        eax  
0041251C  call        @ILT+145(_memset) (411096h) 
00412521  add         esp,0Ch 
00412524  mov         ecx,dword ptr [ebp-104h] 
0041252A  mov         dword ptr [ebp-118h],ecx 
00412530  jmp         main+7Ch (41253Ch) 
00412532  mov         dword ptr [ebp-118h],0 
0041253C  mov         edx,dword ptr [ebp-118h] 
00412542  mov         dword ptr [pS],edx 
S ss2;
S *pS2 = new (&ss2,20) S;
0041259A  push        14h  
0041259C  lea         eax,[ss2] 
0041259F  push        eax  
004125A0  push        4    
004125A2  call        S::operator new (41121Ch) 
004125A7  add         esp,0Ch 
004125AA  mov         dword ptr [ebp-0F8h],eax 
004125B0  mov         ecx,dword ptr [ebp-0F8h] 
004125B6  mov         dword ptr [pS2],ecx 

#15


不可思议,为什么第一种方式会调用了memset呢?

#16


引用 15 楼 loaden 的回复:
不可思议,为什么第一种方式会调用了memset呢?

难道是编译器对new的实现导致?

#17


— If the new-initializer is omitted:
— If T is a (possibly cv-qualified) non-POD class type (or array thereof), the object is  default initialized
(8.5). If T is a const-qualified type, the underlying class type shall have a user-declared
default constructor.
— Otherwise, the object created has indeterminate value. If T is a const-qualified type, or a (possibly
cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of
const-qualified type, the program is ill-formed;
— If the new-initializer is of the form (), the item is  value-initialized (8.5);








To zero-initialize an object of type T means:
— if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
— if T is a non-union class type, each nonstatic data member and each base-class subobject is zeroinitialized;
— if T is a union type, the object’s first named data member89) is zero-initialized;
— if T is an array type, each element is zero-initialized;
— if T is a reference type, no initialization is performed.



To default-initialize an object of type T means:
— if T is a non-POD class type (clause 9),  the default constructor for T is called (and the initialization is
ill-formed if T has no accessible default constructor);
— if T is an array type, each element is default-initialized;
— otherwise, the object is zero-initialized.



To value-initialize an object of type T means:
— if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is
called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is a non-union class type without a user-declared constructor, then every non-static data member
and base-class component of T is value-initialized;
— if T is an array type, then each element is value-initialized;
—  otherwise, the object is zero-initialized
A program that calls for default-initialization or value-initialization of an entity of reference type is illformed.
If T is a cv-qualified type, the cv-unqualified version of T is used for these definitions of zeroinitialization,
default-initialization, and value-initialization.

第一个选中值初始化->再选中零初始化
所以就是在执行了那一句之后,零初始化一次.
(因为要通过默认构造函数初始化的话,必须是user-declared construc)
第二个选中默认初始化
默认初始化中调用默认构造函数...,平凡的,什么也没有做.

#1


还真不一样
S *ps1 = new (&ss1,10)S();
会调用memset
而S *ps2 = new (&ss2,20)S;
不会

#2


引用 1 楼 beginnow 的回复:
还真不一样
S *ps1 = new (&ss1,10)S();
会调用memset
而S *ps2 = new (&ss2,20)S;
不会

我不认为是调用了memset,我认为应该是调用了其默认构造函数所致!
比如:
#include<iostream>
using namespace std;

struct S
{
    S() : i(8) {}
    void* operator new(size_t, void* p, int i)
    {
        ((S*)p)->i = i;
        return p;
    }
    int i;
};

int main()
{
    S ss1;
    S *ps1 = new (&ss1, 10) S();
    cout << ss1.i << ":" << ps1->i << endl;

    S ss2;
    S *ps2 = new (&ss2, 20) S;
    cout << ss2.i << ":" << ps2->i << endl;

    system("pause");
    return 0;
}

输出:
8:8
8:8

#3


引用楼主 yingxunren 的回复:
C/C++ code#include<iostream>usingnamespace std;struct S
{void*operatornew(size_t size,void* p,int i)
    {
        ((S*)p)->i= i;return p;
    }int i;
};int main()
{
    S ss1;
    S*ps1=new (&ss1,10)S();
    cout<< ss1.i<<":"<< ps1->i<< endl;

    S ss2;
    S*ps2=new (&ss2,20)S;
    cout<< ss2.i<<":"<< ps2->i<< endl;

    system("pause");return0;
}

 输出:
 0:0
 20:20
 疑问:为什么第一个输出为0:0,而不是10:10?
       new (&ss1,10)S();  与 new (&ss1,10)S  有什么不同?

那为什么第二个输出时10:10呢?

#4


我觉得这个问题得看new的实现。
你重载了new,而new最终会调用S实例的构造函数。
然后才返回指针。
按常理,应该还有个malloc调用,以调用相应内存中的构造函数。
可是,你没有malloc申请内存。
而是代之以一个已经存在的实例。
这会出现 重复调用构造函数

这种行为会导致不确定性的,楼主你肯定也解释不了我在2楼给的代码。

#5


即使你不加static,重载的new也是static的;(你这种用法类似于replacement new)
重载的new只负责分配内存,在内存被分配后,编译器会产生调用构造函数的代码,
加括号会导致内建类型被初始化为默认值( int 就会被初始化为0 )

#6


果然如楼上所言,而不是不确定性的缘故。如果都加括号,则输出都为0

#7


加括号应该是调用了默认构造函数。

#8


我的理解:方式1强迫系统用不带参数的构造函数来初始化对象。由于你没有提供构造函数,因此系统给你一个缺省的,而10由于构造函数不带参数被直接忽略了。

对于方式2,由于结构有一种逐字段初始化的功能,因此20被赋值给第一个成员变量了

个人认为这种带参数placement new的方法非常不好,可读性太差,楼主谨慎使用,否则一旦有问题就象现在这样只好问人。而和你合作的人,很大程度上还要不停来问你这是啥意思。

#9


不管有没有加括号,都会调用不带参数的构造函数。

#10


引用 7 楼 cattycat 的回复:
加括号应该是调用了默认构造函数。

默认构造函数会给i赋一个不确定的值,而不是0.

#11


楼主这种new合法么?真没见过带额外参数得。

#12


void* operator new(size_t size, void* p, int i)
    {
        ((S*)p)->i = i;
        return p;
    }

确实没什么意义,((S*)p)->i = i;改变后,编译器又会调用默认构造函数重新改变i的值。

#13


本帖最后由 Loaden 于 2009-12-06 23:46:31 编辑
带额外参数是可以的,只是,正常情况下如 4# 所说,会malloc内存,比如:

    static void* operator new(size_t size, int type, const char* file, int line)
    {
        void* p = _malloc_dbg(size, type, file, line);
        if (p == NULL) { qpDbgError(); return NULL; }
        HeapMemory::GetInstance().Add(p);
        return p;
    }

而楼主没有malloc,而是用一个已经构建的实例指针返回。
这会让new在实现时, 重复调用该实例的构造函数。
所以,我认为会有不确定性。

#14


大家看写反汇编的代码就知道了:
S ss;
S *pS = new (&ss,10) S();
004124DE  mov         dword ptr [ebp-110h],4 
004124E8  push        0Ah  
004124EA  lea         eax,[ss] 
004124ED  push        eax  
004124EE  mov         ecx,dword ptr [ebp-110h] 
004124F4  push        ecx  
004124F5  call        S::operator new (41121Ch) 
004124FA  add         esp,0Ch 
004124FD  mov         dword ptr [ebp-104h],eax 
00412503  cmp         dword ptr [ebp-104h],0 
0041250A  je          main+72h (412532h) 
0041250C  mov         edx,dword ptr [ebp-110h] 
00412512  push        edx  
00412513  push        0    
00412515  mov         eax,dword ptr [ebp-104h] 
0041251B  push        eax  
0041251C  call        @ILT+145(_memset) (411096h) 
00412521  add         esp,0Ch 
00412524  mov         ecx,dword ptr [ebp-104h] 
0041252A  mov         dword ptr [ebp-118h],ecx 
00412530  jmp         main+7Ch (41253Ch) 
00412532  mov         dword ptr [ebp-118h],0 
0041253C  mov         edx,dword ptr [ebp-118h] 
00412542  mov         dword ptr [pS],edx 
S ss2;
S *pS2 = new (&ss2,20) S;
0041259A  push        14h  
0041259C  lea         eax,[ss2] 
0041259F  push        eax  
004125A0  push        4    
004125A2  call        S::operator new (41121Ch) 
004125A7  add         esp,0Ch 
004125AA  mov         dword ptr [ebp-0F8h],eax 
004125B0  mov         ecx,dword ptr [ebp-0F8h] 
004125B6  mov         dword ptr [pS2],ecx 

#15


不可思议,为什么第一种方式会调用了memset呢?

#16


引用 15 楼 loaden 的回复:
不可思议,为什么第一种方式会调用了memset呢?

难道是编译器对new的实现导致?

#17


— If the new-initializer is omitted:
— If T is a (possibly cv-qualified) non-POD class type (or array thereof), the object is  default initialized
(8.5). If T is a const-qualified type, the underlying class type shall have a user-declared
default constructor.
— Otherwise, the object created has indeterminate value. If T is a const-qualified type, or a (possibly
cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of
const-qualified type, the program is ill-formed;
— If the new-initializer is of the form (), the item is  value-initialized (8.5);








To zero-initialize an object of type T means:
— if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
— if T is a non-union class type, each nonstatic data member and each base-class subobject is zeroinitialized;
— if T is a union type, the object’s first named data member89) is zero-initialized;
— if T is an array type, each element is zero-initialized;
— if T is a reference type, no initialization is performed.



To default-initialize an object of type T means:
— if T is a non-POD class type (clause 9),  the default constructor for T is called (and the initialization is
ill-formed if T has no accessible default constructor);
— if T is an array type, each element is default-initialized;
— otherwise, the object is zero-initialized.



To value-initialize an object of type T means:
— if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is
called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is a non-union class type without a user-declared constructor, then every non-static data member
and base-class component of T is value-initialized;
— if T is an array type, then each element is value-initialized;
—  otherwise, the object is zero-initialized
A program that calls for default-initialization or value-initialization of an entity of reference type is illformed.
If T is a cv-qualified type, the cv-unqualified version of T is used for these definitions of zeroinitialization,
default-initialization, and value-initialization.

第一个选中值初始化->再选中零初始化
所以就是在执行了那一句之后,零初始化一次.
(因为要通过默认构造函数初始化的话,必须是user-declared construc)
第二个选中默认初始化
默认初始化中调用默认构造函数...,平凡的,什么也没有做.