Как работает размещение новых?
Я написал следующее, думая, что это должно быть segfault во время выполнения. Однако это не так, он работает нормально, и я не понимаю, почему.
#include <cstdlib>
#include <cstdio>
#include <new>
struct MyStruct
{
double *a;
MyStruct()
: a(NULL)
{ printf("Default constructor\n"); }
MyStruct( double *b )
: a(b)
{}
MyStruct( const MyStruct& other )
{
printf("Copy-constructor\n");
if ( a != NULL && *a != 3.14 )
a = other.a;
}
};
int main()
{
double num = 3.14;
MyStruct obj( &num );
void *ptr = ::operator new( sizeof(MyStruct) );
new (ptr) MyStruct(obj);
delete (MyStruct*) ptr; // Calls ~MyStruct
}
Выход:
Copy-constructor
Когда я пишу void *ptr = ::operator new( sizeof(MyStruct) );
Я знаю, что это только выделяет память, и не должен вызывать конструктор по умолчанию. И это не похоже: хорошо.
Когда я пишу new (ptr) MyStruct(obj);
Я бы ожидал, что это будет сегфо, если это сработает так, как я думал. Я думаю, что это эквивалентно ( (MyStruct*) ptr )->MyStruct(obj)
, Если это так, линия if ( a != NULL && *a != 3.14 )
должен достичь *a != 3.14
и Segfault, потому что a
не был инициализирован.
Моя проблема в том что a
похоже, что он не был инициализирован (потому что не было вывода "Конструктор по умолчанию"), но предыдущий по-прежнему не работает по умолчанию. Что мне не хватает?
Вот сгенерированный код сборки (я не знаю, как его прочитать):
.file "placement_new.cpp"
.section .rodata.cst8,"aM",@progbits,8
.align 8
.LCPI0_0:
.quad 4614253070214989087 # double 3.1400000000000001
.text
.globl main
.align 16, 0x90
.type main,@function
main: # @main
.cfi_startproc
# BB#0:
push rbp
.Ltmp2:
.cfi_def_cfa_offset 16
.Ltmp3:
.cfi_offset rbp, -16
mov rbp, rsp
.Ltmp4:
.cfi_def_cfa_register rbp
sub rsp, 48
lea rdi, qword ptr [rbp - 24]
lea rsi, qword ptr [rbp - 16]
movsd xmm0, qword ptr [.LCPI0_0]
mov dword ptr [rbp - 4], 0
movsd qword ptr [rbp - 16], xmm0
call _ZN8MyStructC2EPd
movabs rdi, 8
call _Znwm
mov qword ptr [rbp - 32], rax
mov rax, qword ptr [rbp - 32]
cmp rax, 0
mov qword ptr [rbp - 40], rax # 8-byte Spill
je .LBB0_2
# BB#1:
lea rsi, qword ptr [rbp - 24]
mov rax, qword ptr [rbp - 40] # 8-byte Reload
mov rdi, rax
call _ZN8MyStructC2ERKS_
.LBB0_2:
mov rax, qword ptr [rbp - 32]
cmp rax, 0
mov qword ptr [rbp - 48], rax # 8-byte Spill
je .LBB0_4
# BB#3:
mov rax, qword ptr [rbp - 48] # 8-byte Reload
mov rdi, rax
call _ZdlPv
.LBB0_4:
mov eax, dword ptr [rbp - 4]
add rsp, 48
pop rbp
ret
.Ltmp5:
.size main, .Ltmp5-main
.cfi_endproc
.section .text._ZN8MyStructC2EPd,"axG",@progbits,_ZN8MyStructC2EPd,comdat
.weak _ZN8MyStructC2EPd
.align 16, 0x90
.type _ZN8MyStructC2EPd,@function
_ZN8MyStructC2EPd: # @_ZN8MyStructC2EPd
.cfi_startproc
# BB#0:
push rbp
.Ltmp8:
.cfi_def_cfa_offset 16
.Ltmp9:
.cfi_offset rbp, -16
mov rbp, rsp
.Ltmp10:
.cfi_def_cfa_register rbp
mov qword ptr [rbp - 8], rdi
mov qword ptr [rbp - 16], rsi
mov rsi, qword ptr [rbp - 8]
mov rdi, qword ptr [rbp - 16]
mov qword ptr [rsi], rdi
pop rbp
ret
.Ltmp11:
.size _ZN8MyStructC2EPd, .Ltmp11-_ZN8MyStructC2EPd
.cfi_endproc
.section .rodata.cst8,"aM",@progbits,8
.align 8
.LCPI2_0:
.quad 4614253070214989087 # double 3.1400000000000001
.section .text._ZN8MyStructC2ERKS_,"axG",@progbits,_ZN8MyStructC2ERKS_,comdat
.weak _ZN8MyStructC2ERKS_
.align 16, 0x90
.type _ZN8MyStructC2ERKS_,@function
_ZN8MyStructC2ERKS_: # @_ZN8MyStructC2ERKS_
.cfi_startproc
# BB#0:
push rbp
.Ltmp14:
.cfi_def_cfa_offset 16
.Ltmp15:
.cfi_offset rbp, -16
mov rbp, rsp
.Ltmp16:
.cfi_def_cfa_register rbp
sub rsp, 32
lea rax, qword ptr [.L.str]
mov qword ptr [rbp - 8], rdi
mov qword ptr [rbp - 16], rsi
mov rsi, qword ptr [rbp - 8]
mov rdi, rax
mov al, 0
mov qword ptr [rbp - 24], rsi # 8-byte Spill
call printf
mov rsi, qword ptr [rbp - 24] # 8-byte Reload
cmp qword ptr [rsi], 0
mov dword ptr [rbp - 28], eax # 4-byte Spill
je .LBB2_3
# BB#1:
movsd xmm0, qword ptr [.LCPI2_0]
mov rax, qword ptr [rbp - 24] # 8-byte Reload
mov rcx, qword ptr [rax]
movsd xmm1, qword ptr [rcx]
ucomisd xmm1, xmm0
jne .LBB2_2
jp .LBB2_2
jmp .LBB2_3
.LBB2_2:
mov rax, qword ptr [rbp - 16]
mov rax, qword ptr [rax]
mov rcx, qword ptr [rbp - 24] # 8-byte Reload
mov qword ptr [rcx], rax
.LBB2_3:
add rsp, 32
pop rbp
ret
.Ltmp17:
.size _ZN8MyStructC2ERKS_, .Ltmp17-_ZN8MyStructC2ERKS_
.cfi_endproc
.type .L.str,@object # @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "Copy-constructor\n"
.size .L.str, 18
.ident "Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)"
.section ".note.GNU-stack","",@progbits
2 ответа
Чтение переменной, которая не была инициализирована, является неопределенным поведением. Тогда не исключено, что ваш компилятор устанавливает значение указателя в NULL перед присваиванием, что приведет к короткому замыканию на if
заявление в вашем конструкторе копирования и, таким образом, *a
никогда не будет казнен.
Звонок на размещение new
все в порядке: что должно быть не так: он получает достаточно памяти, чтобы поместить в него объект. Конечно, он должен использовать конструктор копирования, если вы вызываете его obj
в качестве параметра. Появляется ли этот вывод, однако, не в воздухе: printf()
буферизует его память и, поскольку вы вызываете неопределенное поведение после этой конструкции, вызывая delete (MyStruct*)ptr;
т. е. на указатель, не полученный без размещения new
, код может легко потерпеть крах до того, как библиотека очистит буфер (это печатает, что конструктор копирования использовался в моей системе).
Чтобы правильно уничтожить ваш объект, вам нужно использовать что-то вроде этого:
MyStruct* mptr = new(ptr) MyStruct(obj);
mptr->~MyStrucT();
operator delete(ptr);
На самом деле, существует также неопределенное поведение в отношении члена a
во время создания копии: члены не копируются неявно. То есть вы обращаетесь к неинициализированной памяти в конструкторе копирования, который также может делать все, что захочет.