首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 4 真正的物件導向:參考與拷貝語義陷阱深度解析

     編輯:PHP 綜合知識

背景

作者 Johan Persson(JpGraph 圖表庫開發者)總結 PHP 4 進行完整 OO 開發時的語義陷阱。針對有 C++/Java/Eiffel/C# 經驗的開發者,PHP 4 的「拷貝語義」與主流語言的「參考語義」差異極大,極易產生難以發現的 Bug。

核心差異:參考 vs 拷貝語義

PHP 4 中類別變數不是指標,而是物件本身。賦值操作 = 執行的是淺拷貝(建立新物件副本),而非建立別名。

陷阱 1:函數參數傳遞

function SomeFunction($aObj) { $aObj->Set(10); }
$myObj = new MyClass();
SomeFunction($myObj); // 預期修改 $myObj,實際修改副本!

修正:函數簽名加 & 強制參考傳遞:
function SomeFunction(&$aObj)

陷阱 2:參考的參考

$myObject = new SomeClass();
$myRefToObject = &$myObject;      // 正確建立參考
$myCopyRefToObject = $myRefToObject; // 錯誤!這會拷貝物件,而非拷貝參考
$myCopyRefToObject = &$myRefToObject; // 正確:拷貝參考本身

C++ 中參考的參考仍是原物件參考,PHP 4 卻會拷貝物件。

最佳實踐

永遠使用參考傳遞物件或物件賦值:
✅ 效能更好(避免拷貝)
✅ 語義可預測(符合 C++/Java 直覺)
✅ 減少隱性 Bug

建構子中的 $this 參考問題

在建構子中將 $this 註冊為觀察者(Observer 模式)時:

class Display {
    function Display(&$batt) {
        $batt->AddObserver("Notify", $this); // $this 此處是副本!
    }
}
$myBattery = new Battery();
$myDisplay = new Display($myBattery); // 註冊的是 Display 副本

後續 Notify() 更新的是副本屬性,原 $myDisplay 不變。

解決方案

  1. 實例化時加 &:$myDisplay = &new Display($myBattery);(呼叫端需知實作細節)
  2. 二階段建構(JpGraph 採用):
    $myDisplay = new Display();
    $myDisplay->Init($myBattery);

    Init() 內安全使用 &$this

foreach 迴圈的隱性拷貝

// 版本 1:foreach 隱性拷貝
foreach ($this->plots as $p) { $p->Update(); } // 修改副本,原陣列物件不變

// 版本 2:索引迴圈直接操作原物件
for ($i=0; $i

結論:foreach 遍歷物件陣列時會建立副本,需用索引迴圈或 PHP 5 的 foreach ($arr as &$v) 參考模式。

譯者註記

  • [1] OO: Object-Oriented
  • [2] 原文無 C#,譯者個人偏好補充
  • [3] Semantic 譯為「語義」
  • [4] 參考《C++ Gotchas》
  • [5] 此處「類」應指 Instance(實例)
  • [6] 參考 GoF《Design Patterns》Observer 模式
  • [7] 經典買賣馬算術題:兩筆交易各賺 10,共賺 20(選項 C)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved