昨天看到一个同学写了一个访问数据的单例程序,先给大家看看他写的代码:
|
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
|
<?phpclass MYSQL_DEMO {//这个类用来模拟MYSQLI类 public
function close() { echo
"MYSQL_DEMO::close()"; }
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 B 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
|
<?phpclass 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 B 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; } } |
本文通过一个PHP示例展示了单例模式实现中常见的问题——析构时对象已被销毁导致的调用失败。分析了错误原因并提供了正确的单例模式实现。

被折叠的 条评论
为什么被折叠?



