在多数情况下,我们并不需要完全复制一个对象来获得其中的属性。但有一个情况下确实需要:如果你有一个GTK窗口对象,该对象持有窗口的相关资源。
你可能会想复制一个新的窗口,保持所有属性与原来的窗口相同,但必须是一个新的对象(因为如果不是一个新的对象,那么一个窗口中的改变就会影响到另一个窗口)。
还有一种情况:如果对象A中保持着对象B的引用,当你复制对象A时,你想其中使用的对象不再是对象B而是B的一个副本,那么你必须得到对象A的一个副本。
对象复制可以通过clone关键字来完成(如果可能,这将调用对象的__clone()方法)。对象中的__clone()方法不能被直接调用,否则会报一个致命错误(Fatal error: Cannot call __clone() method on objects - use 'clone $obj')。
当对象被复制后,PHP5会对对象的所有的属性执行一个浅复制(shallow copy)。所有的引用属性任然是一个指向原来的变量的引用。
当复制完成时,如果定义了__clone()方法,则新创建的对象(复制生成的对象)中的__clone()方法会被调用,可用于修改属性的值(如果有必要的话)。
下面例子证明了上面两条:
class SubObject
{
}
class MyCloneable
{
public $object1;
public $object2;
function __clone()
{
// 强制复制一份this->object, 否则仍然指向同一个对象
$this->object1 = clone $this->object1;
}
}
$obj = new MyCloneable();
$obj->object1 = new SubObject();
$obj->object2 = new SubObject();
$obj2 = clone $obj;
print("Original Object:\n");
var_dump($obj);
print("Cloned Object:\n");
var_dump($obj2);
运行结果如下,注意看对象编号:
我们会发现,的确是浅复制。
所以我们在做单列类的时候,别人也可能会用克隆的方式来复制我们的对象,怎么办呢,其实很简单,只要我们让__clone()方法不可访问就可以了,代码如下:
class Person {
private static $obj;
public function say()
{
echo '我是人类';
}
public static function construct()
{
if(!self::$obj){
self::$obj = new self();
}
return self::$obj;
}
private function __construct(){}
private function __clone(){}
}
$person = Person::construct();
如果我们克隆对象的时候,就会报一个致命错误(Fatal error: Call to private Person::__clone() from context '')。