#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;
不会
S *ps1 = new (&ss1,10)S();
会调用memset
而S *ps2 = new (&ss2,20)S;
不会
#2
我不认为是调用了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
那为什么第二个输出时10:10呢?
#4
我觉得这个问题得看new的实现。
你重载了new,而new最终会调用S实例的构造函数。
然后才返回指针。
按常理,应该还有个malloc调用,以调用相应内存中的构造函数。
可是,你没有malloc申请内存。
而是代之以一个已经存在的实例。
这会出现 重复调用构造函数。
这种行为会导致不确定性的,楼主你肯定也解释不了我在2楼给的代码。
你重载了new,而new最终会调用S实例的构造函数。
然后才返回指针。
按常理,应该还有个malloc调用,以调用相应内存中的构造函数。
可是,你没有malloc申请内存。
而是代之以一个已经存在的实例。
这会出现 重复调用构造函数。
这种行为会导致不确定性的,楼主你肯定也解释不了我在2楼给的代码。
#5
即使你不加static,重载的new也是static的;(你这种用法类似于replacement new)
重载的new只负责分配内存,在内存被分配后,编译器会产生调用构造函数的代码,
加括号会导致内建类型被初始化为默认值( int 就会被初始化为0 )
重载的new只负责分配内存,在内存被分配后,编译器会产生调用构造函数的代码,
加括号会导致内建类型被初始化为默认值( int 就会被初始化为0 )
#6
果然如楼上所言,而不是不确定性的缘故。如果都加括号,则输出都为0
#7
加括号应该是调用了默认构造函数。
#8
我的理解:方式1强迫系统用不带参数的构造函数来初始化对象。由于你没有提供构造函数,因此系统给你一个缺省的,而10由于构造函数不带参数被直接忽略了。
对于方式2,由于结构有一种逐字段初始化的功能,因此20被赋值给第一个成员变量了
个人认为这种带参数placement new的方法非常不好,可读性太差,楼主谨慎使用,否则一旦有问题就象现在这样只好问人。而和你合作的人,很大程度上还要不停来问你这是啥意思。
对于方式2,由于结构有一种逐字段初始化的功能,因此20被赋值给第一个成员变量了
个人认为这种带参数placement new的方法非常不好,可读性太差,楼主谨慎使用,否则一旦有问题就象现在这样只好问人。而和你合作的人,很大程度上还要不停来问你这是啥意思。
#9
不管有没有加括号,都会调用不带参数的构造函数。
#10
默认构造函数会给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
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
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
难道是编译器对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)
第二个选中默认初始化
默认初始化中调用默认构造函数...,平凡的,什么也没有做.
— 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;
不会
S *ps1 = new (&ss1,10)S();
会调用memset
而S *ps2 = new (&ss2,20)S;
不会
#2
我不认为是调用了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
那为什么第二个输出时10:10呢?
#4
我觉得这个问题得看new的实现。
你重载了new,而new最终会调用S实例的构造函数。
然后才返回指针。
按常理,应该还有个malloc调用,以调用相应内存中的构造函数。
可是,你没有malloc申请内存。
而是代之以一个已经存在的实例。
这会出现 重复调用构造函数。
这种行为会导致不确定性的,楼主你肯定也解释不了我在2楼给的代码。
你重载了new,而new最终会调用S实例的构造函数。
然后才返回指针。
按常理,应该还有个malloc调用,以调用相应内存中的构造函数。
可是,你没有malloc申请内存。
而是代之以一个已经存在的实例。
这会出现 重复调用构造函数。
这种行为会导致不确定性的,楼主你肯定也解释不了我在2楼给的代码。
#5
即使你不加static,重载的new也是static的;(你这种用法类似于replacement new)
重载的new只负责分配内存,在内存被分配后,编译器会产生调用构造函数的代码,
加括号会导致内建类型被初始化为默认值( int 就会被初始化为0 )
重载的new只负责分配内存,在内存被分配后,编译器会产生调用构造函数的代码,
加括号会导致内建类型被初始化为默认值( int 就会被初始化为0 )
#6
果然如楼上所言,而不是不确定性的缘故。如果都加括号,则输出都为0
#7
加括号应该是调用了默认构造函数。
#8
我的理解:方式1强迫系统用不带参数的构造函数来初始化对象。由于你没有提供构造函数,因此系统给你一个缺省的,而10由于构造函数不带参数被直接忽略了。
对于方式2,由于结构有一种逐字段初始化的功能,因此20被赋值给第一个成员变量了
个人认为这种带参数placement new的方法非常不好,可读性太差,楼主谨慎使用,否则一旦有问题就象现在这样只好问人。而和你合作的人,很大程度上还要不停来问你这是啥意思。
对于方式2,由于结构有一种逐字段初始化的功能,因此20被赋值给第一个成员变量了
个人认为这种带参数placement new的方法非常不好,可读性太差,楼主谨慎使用,否则一旦有问题就象现在这样只好问人。而和你合作的人,很大程度上还要不停来问你这是啥意思。
#9
不管有没有加括号,都会调用不带参数的构造函数。
#10
默认构造函数会给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
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
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
难道是编译器对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)
第二个选中默认初始化
默认初始化中调用默认构造函数...,平凡的,什么也没有做.
— 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)
第二个选中默认初始化
默认初始化中调用默认构造函数...,平凡的,什么也没有做.