在本章的开头,我讨论过什么是类以及如何创建和处理复杂对象。现在该讨论内部对象处理的一些内容了。当使用如$x=new class(....)的命令来创建一个对象时,变量$x是对象的一个引用。执行像$x=$y一样的语句时,会发生什么呢?答案很简单:句柄$x所指向的原始对象被销毁,同时调用其析构函数,并且$x指向对象$y。如代码清单1-7所示。
代码清单1-7 当执行$x=$y时
<?php
# 浅复制示例
class test3 {
protected $memb;
function __construct($memb) {
$this->memb = $memb;
}
function __destruct() {
printf("Destroying object %s...\n", $this->memb);
}
}
$x = new test3("object 1");
$y = new test3("object 2");
print "Assignment taking place:\n";
$x = $y;
print "End of the script\n";
?>
当执行时,产生如下输出。
Assignment taking place:
Destroying object object 1...
End of the script
Destroying object object 2...
当执行$x=$y时,对象1在赋值过程中被破坏。那为何对象2也被破坏呢?这个问题的答案很简单:只要对象出了它的作用域,都会调用析构函数。当脚本执行结束,所有幸存的对象将跑出它们的作用域,这样每个对象都将调用其析构函数一次。这也是为何封闭两个print命令之间赋值的原因。此外请注意,尽管事实上底层对象$x和$y有两个引用,但是析构函数只执行一次。针对每一个对象,而不是每一个引用,析构函数被调用一次。这种复制对象的方式称为浅复制(shallow copy),因为只是改变了引用,从来没有创建真正的对象副本。
除了浅复制,还有深复制(deep copy),深复制会创建一个新的对象。这个深复制是使用克隆操作完成的,如代码清单1-8所示。
代码清单1-8 深复制使用克隆操作
<?php
# 深复制示例
class test3a {
protected $memb;
protected $copies;
function __construct($memb, $copies = 0) {
$this->memb = $memb;
$this->copies = $copies;
}
function __destruct() {
printf("Destroying object %s...\n", $this->memb);
}
function __clone() {
$this->memb.= ":CLONE";
$this->copies++;
}
function get_copies() {
printf("Object %s has %d copies.\n", $this->memb, $this->copies);
}
}
$x = new test3a("object 1");
$x->get_copies();
$y = new test3a("object 2");
$x = clone $y;
$x->get_copies();
$y->get_copies();
print "End of the script, executing destructor(s).\n";
?>
深复制在$x=clone $y行完成。但执行该行代码时,对象$y的一个新副本被创建,同时,调用__clone函数,是为了有助于安排新副本为脚本所需要的形式。这个脚本的输出如下所示。
Object object 1 has 0 copies.
Destroying object object 1...
Object object 2:CLONE has 1 copies.
Object object 2 has 0 copies.
End of the script, executing destructor(s).
Destroying object object 2...
Destroying object object 2:CLONE...
在$x里,新创建的副本有一个成员值Object object 2:CLONE,并且副本份数设置为1,这是__clone方法执行的结果。另外请注意,构造函数执行两次,一个为原来的副本,一个为克隆的副本。克隆没有像引用赋值那样频繁使用,但是当需要时,也可以频繁使用克隆。
对象是如何比较的呢?根据比较的标准,有几种情况需要考虑。究竟何时称两个变量$x和$y是“相等的”?下面是三种逻辑同样有效的情况。
同一类对象的所有成员是相等的。
对象是同一类相同对象的引用。
使用其他用户规定的标准。
标准相等运算符==用于测试第一种情形。当且仅当$x和$y相应的成员彼此都相等时,表达式$x==$y成立。
第二种情形,即$x和$y是相同对象的引用,通过特殊运算符测试===(三个连续等号)。当且仅当$x和$y是相同对象的引用时,表达式$x===$y成立。要注意的是,通常的赋值,如$x=$y,将有表达式$x===$y返回true,而克隆操作将打破相等。如果没有自定义的__clone方法,原有的和克隆的将是相等的,就像==运算符定义的相等一样。
关于第三种情形,即一个对相等自定义形成的定义,可以做些什么呢?在这种情况下,必须写一个自定义函数,并比较返回值。当编写的函数提取一个特定类的参数时,它很可能通过在正式参数名称前面列出参数类型,迫使参数为所要求的类型。如下所示。
function test_funct(test3a $a) {….}
在这种情况下,要求参数$a为test3a类型。这只能针对对象类型和数组,通过输入关键词array而不是对象名称来完成。PHP 5仍是一种不太支持类型的语言,总是强制参数类型为经典类型,如PHP就不支持int。