Что так особенное в использовании goto в цепочке if else
Я смотрел видео Alexandrescu, у него есть следующий фрагмент кода:
// an example implementation of a single threaded shared_ptr
~SingleThreadPtr() {
if(!c_) {
soSueMe: delete p_;
} else if(--*c_ == 0) {
delete c_;
goto soSueMe;
}
}
Здесь https://youtu.be/Qq_WaiwzOtI?t=36m44s. Он говорит: "Я использую мою знаменитую конструкцию goto soSueMe" и говорит: "Попробуйте написать это без goto и [..], вы увидите, что это сложно".
Что здесь такого сложного? Не следующее одно и то же, и, очевидно, не сложно и понятно:
// an example implementation of a single threaded shared_ptr
~SingleThreadPtr() {
if(!c_) {
delete p_;
} else if(--*c_ == 0) {
delete c_;
delete p_;
}
}
или это не то же самое (тем самым, в первую очередь, усиливая аргументы против goto
)? Какого рода хакерская черная магическая вуду происходит здесь?
Ответы
Ответ 1
Дело в том, что деструкторы shared_ptr называются относительно часто и обычно встроенными, и это попытка уменьшить размер встроенного деструктора (поскольку goto
намного меньше, чем вызов удаления).
Например, вызов деструктора delete p_
при компиляции может выглядеть примерно так:
LBB5_8:
movq -16(%rbp), %rax ## 8-byte Reload
movq (%rax), %rcx
cmpq $0, %rcx
movq %rcx, -24(%rbp) ## 8-byte Spill
je LBB5_4
movq -24(%rbp), %rax ## 8-byte Reload
movq %rax, %rdi
callq __ZdlPv
LBB5_4:
(где callq __ZdlPv
- это деструктор основного объекта, который, наконец, вызывается).
В то время как goto
выглядит просто так:
LBB5_8:
jmp LBB5_2
Таким образом, путем ветвления, а не повторения оператора delete p_
, код значительно уменьшается.
Эта сопроводительная презентация может оказаться полезной для чтения (хотя и краткой).
Ответ 2
Не проще ли понять следующий код? Компилятор также разработал еще меньше инструкций по сборке:
~SingleThreadPtr() {
if (c_) {
if (--*c_ != 0) {
return;
}
delete c_;
}
delete p_;
}
Инструкции по сборке:
test rcx,rcx
je wmain+0C8h (013F3813D8h)
dec dword ptr [rcx]
jne wmain+0D5h (013F3813E5h)
call qword ptr [__imp_operator delete (013F3831E8h)]
mov rcx,qword ptr [p_ (013F385708h)]
call qword ptr [__imp_operator delete (013F3831E8h)]
Исходный код ниже:
~SingleThreadPtr() {
if(!c_) {
soSueMe: delete p_;
} else if(--*c_ == 0) {
delete c_;
goto soSueMe;
}
}
Создает следующий код сборки:
test rcx,rcx
je wmain+0A5h (013F3813B5h)
dec dword ptr [rcx]
jne wmain+0B9h (013F3813C9h)
call qword ptr [__imp_operator delete (013F3831E8h)]
mov rcx,qword ptr [p_ (013F385708h)]
call qword ptr [__imp_operator delete (013F3831E8h)]
mov rcx,qword ptr [c_ (013F385710h)]