2012-07-11 52 views
8

我開始注意到一些關於Scope::Guard的奇怪事情。DESTROY以意想不到的順序呼叫

  • 如果我未定義一個$guard變量作爲最後的語句子,守衛的子得到 後來稱爲比我期望的那樣。
  • 如果我不民主基金,或者如果我undef $guard後做一些 (東西),它被當引用超出範圍 如記錄調用。我想知道爲什麼。

的代碼也被發現here

my $sClass = 'SGuard'; 
# Uncomment to use Scope::Guard instead: 
# use Scope::Guard; $sClass = 'Scope::Guard'; 

package SGuard; 
sub new { 
    my ($class, $sub) = @_; 
    return bless { sub => $sub }, $class; 
} 

sub DESTROY { 
    my ($self) = @_; 
    $self->{sub}->(); 
} 

package main; 
sub mySub { 
    my $mySubGuard = $sClass->new(sub { 
     print "shouldDestroyFirst\n" 
    }); 

    # Do something - any no-op will do. 
    undef; 

    # Comment out this line and it works 
    undef $mySubGuard; 

    # Or uncomment the next undef line and it works. Any statement(s) or 
    # no-ops will do but we test the return value of mySub to make sure it 
    # doesn't return a reference, so undef... 

    # undef; 
} 
{ 
    my $scopeGuard = $sClass->new(sub { 
     print "shouldDestroyLast\n" 
    }); 

    # Check that mySub returns undef to ensure the reference *did* go out 
    # of scope 
    printf "mySub returned a %sdefined value\n", defined mySub() ? "" : "un"; 
} 
print "done\n"; 

在代碼中,我做了我自己的可憐人的Scope::GuardSGuard以上) 只是爲了讓這個例子儘可能簡單。您也可以使用Scope::Guard 並獲得完全相同的結果,至少對我來說是意想不到的。

我期待這裏面mySub()$mySubGuard應銷燬第一 和調用mySub()應銷燬 最後的範圍$scopeGuard。所以得到的輸出,如:

shouldDestroyFirst 
mySub returned a undefined value 
shouldDestroyLast 
done 

我得到上面的輸出,如果我使用mySub undef $mySubGuard線。 如果我不使用mySub undef $mySubGuard線,我得到以下的輸出:

mySub returned a undefined value 
shouldDestroyLast 
shouldDestroyFirst 
done 

所以,它看起來像局部變量的外部範圍被破壞後$mySubGuardmySub()被破壞 。

爲什麼行爲不同只是因爲我undef一個變量,即將 超出範圍?之後爲什麼要做 ?

回答

1

看起來好像是某種優化。如果你的undef是一個變量,並且之後不使用它,它會被放入某種隊列來檢查魔法或其他東西。但是如果你以後做了任何事情,那麼它就會做到這一點。

此外,可能有一個錯誤,因爲你是undef - 它說在一個「返回上下文」,有一個變量的副本正在檢查的東西。也許Perl保留了一個引用,它稍後會清理並結束調用範圍。

您還會注意到,任何其他陳述後undef - 警衛導致預期的行爲。包括PBP推薦的結尾所有子目錄與return。 Damian明確的理由之一是避免了意想不到的副作用。

問題已解決:與undef一樣容易 - 您可以直接運行其處理程序。不要取消警衛作爲子的最後(隱式返回)語句。有一個明確的解除警衛的理由,比如你想運行他們的處理程序,然後進一步處理。

這是令人困惑和意想不到的,但絕對不是應該出現在完成或標準化的代碼中。