The code below illustrates the destruct() being called twice. I'd like to know why?
class A {
function hi(){ echo 'hi'; }
function __destruct(){
echo 'destroy';
}
}
class B{
public $this_ = '';
function __construct(){
$this->this_ = new A;
}
function __call($method, $params) {
return call_user_func_array(array($this->this_, $method), $params);
}
}
$b = new B;
$b->__destruct();
output:
destroydestroy
EDIT
Both zneak and TomcatExodus is correct. If I simply:
[..code..]
$b = new B;
$b->__destruct();
print 'end of script';
开发者_StackOverflow中文版
The output will show:
destroyend of scriptdestroy
Invoking destruct doesn't destroy the object. You call it with __destruct()
the first time, then when the PHP script terminates, it calls it again at cleanup.
If you're looking to destroy the object prior to script termination, unset()
it. You should see only a single destruct call made.
Specifically, your class B
creates a self contained instance of class A
. Since B
also routes method calls via __call()
to the A
object, that's why a __destruct()
call on B
is calling __destruct()
on A
; B
has no destructor defined and passes the call up.
Since B
has no __destruct
method, the __call
method is called instead (you can verify this by adding something like echo "calling $method"
to your __call
method), and then it is then forwarded to your A
object.
However, calling __destruct
doesn't destroy the object: it just calls the cleanup code that should be associated to its destruction. So once you get at the end of your script, when the A
object is actually destroyed, its __destruct
method is called again.
If you want to delete your B
object, use unset($b)
.
Calling the destructor
manually is one of the worst ideas, especially when dealing with others' code. An object has a logic that starts on construct
, goes through methods
and ends on destruct
. On destruct
, the object might require some cleanup and variables
might be invalidated. When destruct
is called internally, as a result of a succeeded unset($Object)
, the object is no longer available. When you do it manually, the object is still within reach but has no internal variable support if it did some cleanup itself.
Now think how it would be if you called a method on the object that relies on data that you have invalidated prematurely after invoking
destruct
manually. It breaks the entire logic! So alwaysunset()
and let PHP do its thing.
Manual destruct (placement delete :)) is awesome in C++ if you know what you're doing, especially combined with placement new. But you have to guard yourself throughout the entire implementation and make sure you actually have the data when a method is called. And you have to guard yourself in the destruct if you manage memory manually, not to delete pointers TWICE and crash in the process.
Memory management is so cool in C++! I hate GC (garbage collectors) :)
--- RANT OVER ---
You are invoking a destructor manually; but that does not mean that you are deleting the object. You are just invoking a method, and that method is just like any other.
The call to $b->__destruct()
calls $b->this_
's destructor, because $b
has no explicit destructor method.
When the script finalizes, the Zend Engine calls all instantiated objects their destructors and then performs a routine cleanup, which involves calling the contained objects' destructors, i.e., after destroying $b
, $b->this_
has to be cleared and, to do that, the Engine calls its constructor automatically.
Note that the second call is not due to the destruction of $b
but due to the destruction of the A
instance.
There is NO handicap in destroying an object manually, and it does free its resources (unless the object is being shared, and then the GC won't destroy it unless there are no more references to it; in PHP there are no weak-references).
Example of a GC working: http://codepad.org/7JDBoOKY
The objects gets destructed before the code finalizing. If it wasn't then the order of the output would have been inverted.
精彩评论