在 IF ELSE 的链中使用 goto 有什么特别之处

what so special about a use of goto in a chain of if else

本文关键字:什么 goto ELSE IF      更新时间:2023-10-16

我正在看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的论点)?这里发生了什么黑客的黑魔法巫毒教?

这里的重点是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_语句,代码大小显著减小。

这个伴随的演示文稿可能会证明是有用的阅读(虽然很简洁)。

下面的代码不是更容易理解吗?编译器产生的汇编指令也更少:

~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)]