php物件導向之解構函式和物件參照

2020-07-16 10:05:54

PHP物件導向之解構函式和物件參照


本文學習目標:

1、了解解構函式的定義

2、了解解構函式的作用

3、了解解構函式的特點

4、掌握物件參照賦值的概念和特點

(一)、解構函式

1、定義它是一個特殊的函數

public function destruct(){}

2、作用:清理物件,釋放記憶體

3、特點:

1、自動執行,而非手動呼叫

2、類一旦定義了解構函式,程式結束前就會銷毀該類下的所有範例物件

3、在應用程式結束前的最後一刻執行,除非一些特殊情況,比如第4點,或者當物件的生命週期結束以後也會自動執行

4、 一旦我們手動的銷毀一個物件,系統會自動的觸發該物件的解構函式

特別注意一個特殊的情況:就是如果物件還被其他物件參照的情況下,它的解構函式也不會被觸發

5、在物件的生命週期結束前執行

6、應用程式結束前的最後一刻,會銷毀掉還未銷毀的物件,已經銷毀的物件不會再次被銷毀

進一步得出,一個物件的解構函式只能執行1次,不會執行多次

7、在php中如果我們不為類定義個解構函式,那麼php會自動的為類建立一個解構函式,然後在

程式結束前呼叫預設的解構函式,但是一旦定義了解構函式,就會執行我們寫的解構函式

進一步我們就可以在自己的解構函式裡寫自己的業務程式碼

比如如果程式使用了印表機資源,我們可以銷毀物件前釋放印表機資源

相關的程式碼如下:

<?php

 class NbaPlayer{
   
    public $name  = "";//姓名
    public $height = "";//身高
    public $weight = "";//體重
    public $team = "";//團隊
    public $playerName = "";//球員號碼

    
    public function __construct( $name,$height,$weight,$team,$playerName ){
        $this->name = $name;
        $this->height=$height;
        $this->weight = $weight;
        $this->team = $team;
        $this->playName = $playerName;
        echo "建構函式執行了,當前物件是{$this->name}<br/>";
    }
    //解構函式
    public function __destruct(){
        echo "銷毀物件".$this->name."<br/>";
    }
   
   //跑步
    public function run(){
        echo "跑步中<br/>";
    }
    //跳躍
    public function jump(){
        echo "跳躍<br/>";
    }
    //運球
    public function dribble(){
        echo "運球<br/>";
    } 
    //傳球
    public function pass(){
        echo "傳球<br/>";
    }
    //投籃
    public function shoot(){
        echo "投籃<br/>";
    }
    //扣籃
    public function dunk(){
        echo "扣籃<br/>";
    }

 }
 //建立喬丹物件
$jordon = new NbaPlayer("喬丹","1.98米","98公斤","公牛","23");
//輸出喬丹物件
echo "名稱= ".$jordon->name."<br/>";
//讓喬丹跑步
$jordon->run();

//建立科比物件
$kobe = new NbaPlayer("科比","2米","93公斤","湖人","24");
//建立詹姆斯物件
$james = new NbaPlayer("詹姆斯","2.03米","120公斤","熱火","6");
$james1 = new NbaPlayer("詹姆斯1","2.03米","120公斤","熱火","6");
$james2 = new NbaPlayer("詹姆斯2","2.03米","120公斤","熱火","6");

$jordon = null;//手動的銷毀了物件 ,此時喬丹物件的解構函式將會被觸發

$kobe = null;//手動的銷毀了物件 ,此時科比物件的解構函式將會被觸發


echo "<b>程式結束完畢</b><br/>";
?>

接下來,我程式碼修改如下,新增一個Mysql資料庫連線類,然後在NbaPlayer建構函式裡面呼叫它

這是Mysql.class.php,裡面定義了一個解構函式

<?php
//資料庫類
class Mysql{
    //定義屬性
    public $conn = "";
    //建構函式
    public function __construct( ){
        //初始化行為 初始化方法
        $this->initConn();
    }
    //解構函式 銷毀資料庫連線
    public function __destruct(){
        //銷毀連線
        if( $this->conn ){
            mysqli_close( $this->conn );
            echo "銷毀了連線<br/>";
        }
    }
    
    //定義方法
    //建立公共的方法 獲取資料庫連線
    public function initConn(){
        $config = Array(
            "hostname"=>"127.0.0.1",
            "database"=>"Nbaplayer",
            "username"=>"root",
            "password"=>"root"
        );
        $this->conn = mysqli_connect( $config['hostname'],$config['username'] ,$config['password'],
                $config['database']);
    }
}
?>

接下來還是定義個NbaPlayer類,但是為了突出重點,所以NbaPlayer類我會簡寫如下:

<?php
 require_once "Mysql.class.php";
 class NbaPlayer{
   
    public $name  = "";//姓名
    public $height = "";//身高
    public $weight = "";//體重
    public $team = "";//團隊
    public $playerName = "";//球員號碼
    public $conn = "";//新增一個資料庫連線屬性

    
    public function __construct( $name,$height,$weight,$team,$playerName ){
        $this->name = $name;
        $this->height=$height;
        $this->weight = $weight;
        $this->team = $team;
        $this->playName = $playerName;
        //初始化資料庫連線屬性
        $mysql = new Mysql();
        $this->conn = $mysql->conn;
        
    }
    //新增獲取所有Nba球員的方法
     public function getAll(){
         //建立資料庫連線
        $conn = $this->conn;
        //寫sql
        $sql = " select * from ".$this->tableName;
        //執行sql
        $result = mysqli_query( $conn,$sql );
        //獲取資料
        // mysqli_fetch_all($result)//特點:不會包含欄位名
        $list = Array();
        while( $row = mysqli_fetch_assoc(  $result ) ){
            $list[] = $row;
        }
        //返回資料
        return $list;
     }

 }
 //建立喬丹物件
$jordon = new NbaPlayer("喬丹","1.98米","98公斤","公牛","23");

$list = $jordon->getAll();

echo "<b>程式結束完畢</b><br/>";
?>

當你執行,你會發現錯誤,會發現連線已經被銷毀,在getAll函數呼叫之前,也就是說,一旦 範例化了 $jordon = new NbaPlayer("喬丹","1.98米","98公斤","公牛","23");

資料庫連線物件就銷毀了,其實如果你去偵錯,你會發現,建構函式裡的mysql物件其實是在NbaPlayer類別建構函式的最後一個}執行完成後,它就會被銷毀,為什麼呢

這其實就涉及到變數作用域的問題,因為mysql物件是在建構函式裡定義的,所以外面是無法存取到它的,所以一旦建構函式執行完成後,系統會認為它將不再有用,所以就會

把它清理掉,從而執行它的解構函式,所以最終你去呼叫getAll方法的時候,資料庫連線都早已斷開,自然無法再去執行sql,

其實想要解決掉這個問題,那麼就需要了解物件參照,我們可以結合物件參照來解決這個問題,接下來先來了解物件參照

(二)、物件參照

總結:

1、變數1=變數2=物件 會建立物件的2個獨立參照,但是他們指向的還是同一個物件,所以還是會互相影響

2、變數1=&變數2 只會建立物件的一個參照,因為它們使用同一個物件參照

3、一個物件有沒有用,就要看它是否完全沒有被參照了,如果沒有任何變數參照它,它就真的沒用了,

才會被銷毀,進而它的解構函式才會得以執行

4、變數1=clone 變數2=物件,不會建立物件的新參照,而是仿造了一個新的該物件,具有該物件通用的屬性和方法

相關程式碼如下:

<?php

 class NbaPlayer{
   
    public $name  = "";//姓名
    public $height = "";//身高
    public $weight = "";//體重
    public $team = "";//團隊
    public $playerName = "";//球員號碼

    
    public function __construct( $name,$height,$weight,$team,$playerName ){
        $this->name = $name;
        $this->height=$height;
        $this->weight = $weight;
        $this->team = $team;
        $this->playName = $playerName;
        echo "建構函式執行了,當前物件是{$this->name}<br/>";
    }
    public function __destruct(){
        echo "銷毀物件".$this->name."<br/>";
    }
   
   //跑步
    public function run(){
        echo "跑步中<br/>";
    }
    //跳躍
    public function jump(){
        echo "跳躍<br/>";
    }
    //運球
    public function dribble(){
        echo "運球<br/>";
    } 
    //傳球
    public function pass(){
        echo "傳球<br/>";
    }
    //投籃
    public function shoot(){
        echo "投籃<br/>";
    }
    //扣籃
    public function dunk(){
        echo "扣籃<br/>";
    }

 }
 //建立喬丹物件
$jordon = new NbaPlayer("喬丹","1.98米","98公斤","公牛","23");
//輸出喬丹物件
echo "名稱= ".$jordon->name."<br/>";
//讓喬丹跑步
$jordon->run();

//建立科比物件
$kobe = new NbaPlayer("科比","2米","93公斤","湖人","24");
//建立詹姆斯物件
$james = new NbaPlayer("詹姆斯","2.03米","120公斤","熱火","6");
$james1 = new NbaPlayer("詹姆斯1","2.03米","120公斤","熱火","6");
$james2 = new NbaPlayer("詹姆斯2","2.03米","120公斤","熱火","6");

 $jordon1 = $jordon;//&符號表示左邊物件和右邊物件其實就是一個物件
 $jordon = null;//手動的銷毀了物件 


echo "<b>程式結束完畢</b><br/>";
?>

重點解析:本來不加$jordon1 = $jordon;當程式執行到$jordon=null,喬丹物件的解構函式將會被執行,也就是說 「銷毀物件喬丹」在「程式結束完畢」 前顯示
但是加了這句以後,你會發現執行到$jordon=null的時候,喬丹物件的解構函式並沒有馬上執行,而是到應用程式結束後才被系統自動執行 ,也就是說

「銷毀物件喬丹」在「程式結束完畢」 後顯示

為什麼會這樣:

接下來就來具體分析$jordon1 = $jordon 這行程式碼到底讓系統做了什麼事情

1.png

結合上面的程式碼,其實我們寫的程式碼順序是

$jordon = new NbaPlayer("喬丹","1.98米","98公斤","公牛","23");

$jordon1 = $jordon;

那麼$jordon = new NbaPlayer("喬丹","1.98米","98公斤","公牛","23");

說明就是

1、建立了喬丹物件

2、建立了一個變數,變數名叫jordon

3、創了一個喬丹物件的獨立參照,就是上圖的箭頭

然後$jordon1 = $jordon;

說明就是

1、建立了一個新的變數,名叫jordon1

2、又建立了一個喬丹物件的獨立參照,就是上圖的第二個箭頭

那麼說明 喬丹物件此時被兩個變數參照,當我們$jordon=null 的時候,喬丹物件還被jordon1變數參照,所以此時喬丹物件還有用,還有用就不能當做垃圾清理掉,

所以這就可以解釋上面的問題,喬丹物件 在最後 才會被系統銷毀,所以要看一個物件是否有用,要看它到底還存不存在變數參照,如果完全不存在變數參照了,那麼這個

物件才可以被視作完全無用,它的解構函式才會被執行

好這是物件參照賦值的一種形式,還有另外一種 就是 =&

程式碼如下:

<?php

 class NbaPlayer{
   
    public $name  = "";//姓名
    public $height = "";//身高
    public $weight = "";//體重
    public $team = "";//團隊
    public $playerName = "";//球員號碼

    
    public function __construct( $name,$height,$weight,$team,$playerName ){
        $this->name = $name;
        $this->height=$height;
        $this->weight = $weight;
        $this->team = $team;
        $this->playName = $playerName;
        echo "建構函式執行了,當前物件是{$this->name}<br/>";
    }
    public function __destruct(){
        echo "銷毀物件".$this->name."<br/>";
    }
   
   //跑步
    public function run(){
        echo "跑步中<br/>";
    }
    //跳躍
    public function jump(){
        echo "跳躍<br/>";
    }
    //運球
    public function dribble(){
        echo "運球<br/>";
    } 
    //傳球
    public function pass(){
        echo "傳球<br/>";
    }
    //投籃
    public function shoot(){
        echo "投籃<br/>";
    }
    //扣籃
    public function dunk(){
        echo "扣籃<br/>";
    }

 }
 //建立喬丹物件
$jordon = new NbaPlayer("喬丹","1.98米","98公斤","公牛","23");
//輸出喬丹物件
echo "名稱= ".$jordon->name."<br/>";
//讓喬丹跑步
$jordon->run();

//建立科比物件
$kobe = new NbaPlayer("科比","2米","93公斤","湖人","24");
//建立詹姆斯物件
$james = new NbaPlayer("詹姆斯","2.03米","120公斤","熱火","6");
$james1 = new NbaPlayer("詹姆斯1","2.03米","120公斤","熱火","6");
$james2 = new NbaPlayer("詹姆斯2","2.03米","120公斤","熱火","6");

 $jordon1 = &$jordon;//&符號表示左邊物件和右邊物件其實就是一個物件
 $jordon = null;//手動的銷毀了物件 


echo "<b>程式結束完畢</b><br/>";
?>

當我們把上面的程式碼僅僅加上一個 &,也就是把$jordon1 = $jordon 改成 $jordon1 =& $jordon,你會發現結果又變了

變成什麼呢,就是當執行$jordon=null的時候,喬丹物件的解構函式還是被執行了,為什麼呢

我們再來看下 $jordon1 = & $jordon 做了什麼事情

2.png

系統所做事情如下:

1、建立變數,jordon1

2、然後不會再次建立喬丹物件參照,$jordon1通過$jordon變數來使用同一個物件參照來存取喬丹物件

所以此時喬丹物件,只有一個物件參照,不是2個,所以當我們$jordon=null的時候,其實就是銷毀了那唯一的物件參照,進而導致喬丹物件完全沒有了物件參照,所以他的解構函式此時會被觸發執行

不管是$jordon1=$jordon 還是 $jordon1=&jordon ,修改任意變數裡面的名稱,都會導致另外變數的名稱被修改掉,因為他們都是參照的同一個喬丹物件

其實物件賦值,除了上面2種方式外,還有第三種,就是 clone(淺複製) ,也就是比如$jordon1 = clone $jordon;

那這樣的賦值方式它究竟又是什麼意思呢,其實相當於 仿造了 一個喬丹物件

接下來我們通過程式碼演示

<?php

 class NbaPlayer{
   
    public $name  = "";//姓名
    public $height = "";//身高
    public $weight = "";//體重
    public $team = "";//團隊
    public $playerName = "";//球員號碼

    
    public function __construct( $name,$height,$weight,$team,$playerName ){
        $this->name = $name;
        $this->height=$height;
        $this->weight = $weight;
        $this->team = $team;
        $this->playName = $playerName;
        // echo "建構函式執行了,當前物件是{$this->name}<br/>";
    }
    public function __destruct(){
        echo "銷毀了物件".$this->name."<br/>";
    }
   
   //跑步
    public function run(){
        echo "跑步中<br/>";
    }
    //跳躍
    public function jump(){
        echo "跳躍<br/>";
    }
    //運球
    public function dribble(){
        echo "運球<br/>";
    } 
    //傳球
    public function pass(){
        echo "傳球<br/>";
    }
    //投籃
    public function shoot(){
        echo "投籃<br/>";
    }
    //扣籃
    public function dunk(){
        echo "扣籃<br/>";
    }

 }
 //建立喬丹物件
$jordon = new NbaPlayer("喬丹","1.98米","98公斤","公牛","23");

$jordon1 = clone $jordon;
$jordon1 = null;
$jordon = null;

echo "應用程式結束<br/>";


?>

執行結果如下:

3.png

所以$jordon1=clone $jordon;其實就是 仿造了一個喬丹物件,擁有和喬丹物件一樣的屬性和方法

到目前為止,我們了解了物件參照,那麼結合物件參照,我們如何解決最開始的那個問題呢?

大家思考一下再看下面的解決方案

....................................

好,接下來我們來看上面我們遇到的問題

問題是:mysql物件在建構函式執行完畢後,就被銷毀了,導致後面getAll函數裡的資料庫連線無法使用

解決思路:

1.mysql物件在建構函式執行完成後,就被銷毀了,說明mysql物件此時沒有了物件參照

2.那我們就要讓他在建構函式執行完成後,依然存在物件參照,那麼mysql物件就還有用,還有用就不會被銷毀

3.在建構函式裡,建立mysql物件的參照,讓它在建構函式執行完成後,這個參照依然存在

4.我們可以定義個類的新屬性比如$mysql,然後讓$this->mysql = $mysql;這樣就建立了一個mysql物件的獨立參照,而且當建構函式執行完成後

類的mysql屬性還有用,所以這個物件參照還有用,進而mysql物件還有用,就不會被銷毀

具體程式碼如下:

<?php
 require_once "Mysql.class.php";
 class NbaPlayer{
   
    public $name  = "";//姓名
    public $height = "";//身高
    public $weight = "";//體重
    public $team = "";//團隊
    public $playerName = "";//球員號碼
    public $conn = "";//新增一個資料庫連線屬性
    public $mysql = "";//新增一個mysql屬性

    
    public function __construct( $name,$height,$weight,$team,$playerName ){
        $this->name = $name;
        $this->height=$height;
        $this->weight = $weight;
        $this->team = $team;
        $this->playName = $playerName;
        //初始化資料庫連線屬性
        $mysql = new Mysql();
        $this->conn = $mysql->conn;
        //解決問題的重點程式碼
        $this->mysql = $mysql;//加了這行程式碼,getAll中的conn 資料庫連線就不會銷毀
        
    }
    //新增獲取所有Nba球員的方法
     public function getAll(){
         //建立資料庫連線
        $conn = $this->conn;
        //寫sql
        $sql = " select * from ".$this->tableName;
        //執行sql
        $result = mysqli_query( $conn,$sql );
        //獲取資料
        // mysqli_fetch_all($result)//特點:不會包含欄位名
        $list = Array();
        while( $row = mysqli_fetch_assoc(  $result ) ){
            $list[] = $row;
        }
        //返回資料
        return $list;
     }

 }
 //建立喬丹物件
$jordon = new NbaPlayer("喬丹","1.98米","98公斤","公牛","23");

$list = $jordon->getAll();

echo "<b>程式結束完畢</b><br/>";
?>

好了,最後我們再稍微做下總結:

1、了解了解構函式的定義,它其實就是一個特殊函數

2、了解了解構函式的作用,就是8個字,清理物件,釋放記憶體

3、了解了解構函式的特點,特點有點多,主要7點

4、知道了物件參照的3種賦值方式,一個是=一個是=&,還有一個是clone

以上就是php物件導向之解構函式和物件參照的詳細內容,更多請關注TW511.COM其它相關文章!