苦逼程序员,你还在这样写单例吗

    昨天看到一个同学写了一个访问数据的单例程序,先给大家看看他写的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
class MYSQL_DEMO {//这个类用来模拟MYSQLI类
     public function close() {
         echo "MYSQL_DEMO::close()" ;
     }

}


//DB类是用于产生单例

class DB {
     private static $dao ;
 
     public static function getInstance() {
         if (!self:: $dao ) {
             self:: $dao = new MYSQL_DEMO();
         }
 
         return self:: $dao ;
     }
}
 
class A {
     private static $dao ;
 
     public function __construct() {
         self:: $dao = DB::getInstance();   
     }
}
 
class extends A{
}
 
class C extends A {
 
}
 
$tmp = new B();
$tmp = new C();

我们说折断代码是有问题的,您知道问题出自那吗?


我们分析一下这个程序,MYSQL_DEMO就是mysqli类,DB类是数据链接类,主要目的就是建立数据库链接,DB类有一个静态变量$dao用来记录MYSQL_DEMO对象,并且是DB::getIntance()的返回值,也许您还没有意识到错误,因为仅仅这样写的话程序是能够正常执行的。


但是就在昨天,这个同学想加上mysql的close函数用于及时释放内存,于是代码就变成了下面的样子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
class MYSQL_DEMO {
     public function close() {
         echo "MYSQL_DEMO::close()" ;
     }
}
 
class DAO {
     private static $dao ;
 
     public static function getInstance() {
         if (!self:: $dao ) {
             self:: $dao = new MYSQL_DEMO();
         }
 
         return self:: $dao ;
     }
}
 
class A {
     private static $dao ;
 
     public function __construct() {
         self:: $dao = DAO::getInstance();   
     }
 
     public function __destruct() { //新加语句
         self:: $dao ->close(); //新加语句
         self:: $dao = null; //新加语句
     } //新加语句
}
 
class extends A{
}
 
class C extends A {
 
}
 
$tmp = new B();
$tmp = new C();

此时看上去也没有问题,但是当我们运行程序的时候发现,程序报错了,错误如下:

MYSQL_DEMO::close()
Fatal error: Call to a member function close() on a non-object in /home/work/Webroot/index.php on line 28


读者可以自己分析一下错误的原因。。。


错误分析如下:

首先 $tmp = new B();  B的父类A属性dao对应的值为mysql_demo对象

我们在看一下$tmp = new C();这句话分两步执行

1。 new C();这个时候也没有问题,此时的C的父类A属性dao对应的值仍然为B父类A属性对应的mysql_demo

2。 $tmp = new C();对$tmp赋值,这个时候就有问题了,赋值完成后B类就没有任何引用了,那么就会掉用B类的析构函数,这个过程中也会掉用A类的析构函数,掉用完成后B的父类A属性dao对应的mysql_demo对象就没有了。也就是说C父类A属性dao对应的mysql_demo就没有了


当整个脚本执行完成后, 会掉用C的析构函数,相应的需要掉用A的析构函数,但此时A的析构函数中执行了self::$dao->close();

上面我们分析到其实self::$dao这个变量指向的对象已经被销毁了。如果再次掉用close函数肯定会报错唠


所有这些都是因为单例没有写对


正确的单例写法如下:

1
2
3
4
5
6
7
8
9
10
11
class A {   
     public static function getInstance()   
     {   
         if (! (self:: $_instance instanceof self) )  
         {   
             self:: $_instance = new self();    //请注意,这应应该是self
        
         return self:: $_instance ;   
       
     }  
}







评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值