Ker*_* SB 10 php exception stack-unwinding
我正在学习PHP类和异常,并且,从C++背景来看,以下内容让我感到奇怪:
当派生类的构造函数抛出异常时,似乎基类的析构函数不会自动运行:
class Base
{
public function __construct() { print("Base const.\n"); }
public function __destruct() { print("Base destr.\n"); }
}
class Der extends Base
{
public function __construct()
{
parent::__construct();
$this->foo = new Foo;
print("Der const.\n");
throw new Exception("foo"); // #1
}
public function __destruct() { print("Der destr.\n"); parent::__destruct(); }
public $foo; // #2
}
class Foo
{
public function __construct() { print("Foo const.\n"); }
public function __destruct() { print("Foo destr.\n"); }
}
try {
$x = new Der;
} catch (Exception $e) {
}
Run Code Online (Sandbox Code Playgroud)
这打印:
Base const.
Foo const.
Der const.
Foo destr.
Run Code Online (Sandbox Code Playgroud)
另一方面,如果构造函数(at )中存在异常,则会正确执行成员对象的析构函数#1.现在我想知道:如何在PHP的类层次结构中实现正确的范围展开,以便在发生异常时正确销毁子对象?
此外,似乎在所有成员对象都被销毁(at #2)之后无法运行基础析构函数.也就是说,如果我们删除行#1,我们得到:
Base const.
Foo const.
Der const.
Der destr.
Base destr.
Foo destr. // ouch!!
Run Code Online (Sandbox Code Playgroud)
如何解决这个问题?
更新:我仍然愿意接受进一步的贡献.如果某人有充分的理由为什么PHP对象系统永远不需要正确的破坏序列,我会给出另一个赏金(或者只是为了任何其他令人信服的争论答案).
我想解释为什么PHP以这种方式运行以及为什么它实际上使(某些)有意义.
在PHP中,只要没有对象的引用,对象就会被销毁.可以通过多种方式移除引用,例如通过unset()变量,通过保留范围或作为关闭的一部分.
如果你理解了这一点,你可以很容易地理解这里发生了什么(我将首先解释没有异常的情况):
$x(对实例Der)创建的引用时,对象将被销毁.$this->foo了对Foo实例的引用(作为销毁成员字段的一部分.)Foo,因此它也被销毁并且析构函数被调用.想象一下,这不会以这种方式工作,并且会在调用析构函数之前销毁成员字段:您无法在析构函数中访问它们.我严重怀疑在C++中存在这样的行为.
在Exception的情况下,您需要了解对于PHP,从来没有真正存在类的实例,因为构造函数永远不会返回.你怎么能破坏从未构建过的东西?
我如何解决它?
你没有.你需要一个析构函数这一事实可能是设计糟糕的表现.而破坏秩序对你很重要的事实甚至更多.