$this在继承方法上的行为


Behavior of $this on inherited methods

我一直认为我理解OOP是如何工作的(我已经使用它很多年了),但有时我意识到有些概念对我来说仍然不太清楚。

我刚刚遇到一个关于PHP中方法可见性的问题。接受的答案解释了PHP中的子类不能覆盖私有方法。好吧,这很有道理。然而,这个例子让我思考了PHP中的内部继承机制,以及$this在继承方法上的行为方式。

请考虑以下代码(PHP手册中的示例,也包含在上面提到的问题中):

class Bar 
{
    public function test() {
        $this->testPrivate();
        $this->testPublic();
    }
    public function testPublic() {
        echo "Bar::testPublic'n";
    }
    private function testPrivate() {
        echo "Bar::testPrivate'n";
    }
}
class Foo extends Bar 
{
    public function testPublic() {
        echo "Foo::testPublic'n";
    }
    private function testPrivate() {
        echo "Foo::testPrivate'n";
    }
}
$myFoo = new foo();
$myFoo->test();
/* 
Output:
Bar::testPrivate
Foo::testPublic
*/

现在考虑一下PHP手册的摘录:

当从对象上下文中调用方法时,伪变量$this可用$这是对调用对象的引用(通常是该方法所属的对象,但如果从辅助对象的上下文静态调用该方法,则可能是另一个对象)。

解释称"$this是对调用对象的引用",即$myFoo。所以我期望$myFoo->test()总是调用Foo::testPrivate,而从不调用Bar::testPrivate(除非$myFooBar的实例)。我用get_class测试了$this,它总是返回Foo,即使是从Bar::testPrivateBar::test内部。然而,当Bar::test调用$this->testPrivate()时,$this的行为类似于Bar的实例。

这真的很令人困惑,我正试图理解为什么会这样!

我认为继承的方法(publicprotected)是以某种方式从基类复制到子类的。私有方法根本不会被复制。但这个例子表明它不是这样工作的。看起来Foo的实例保留了Bar的内部实例,并在必要时将方法调用委托给它。

我想在这里学到一些东西,只有当事情对我有意义的时候我才会学到。写完这一切之后,我想我可以用两个问题来概括:

  1. 有人能简要解释一下继承在PHP内部是如何工作的吗?或者至少给我指一篇关于这方面的文章或文档?

  2. 这里讨论的行为或$this是否也存在于其他OO语言中,或者它是PHP特有的?

PHP中的继承与大多数面向对象语言中的继承工作方式相同。

当您有一个"虚拟"方法时,该方法不会直接绑定到调用者。相反,每个类都包含一个小的查找表,上面写着"这个方法名绑定到那个实现"。所以,当你说$this->testPublic()时,实际发生的是PHP:

  • 获取当前类的虚拟表
  • 在该表中查找testPublic的虚拟表条目
  • 调用该查找所指向的方法

由于Foo覆盖testPublic,因此其虚拟表包含指向Foo::testPublictestPublic条目。

现在,有了私有方法,行为就不一样了。由于正如您正确阅读的那样,私有方法不能被重写,因此调用私有方法永远不会导致虚拟表查找。也就是说,私有方法不能是虚拟的,必须始终在使用它们的类中定义。

因此,效果是在声明时绑定了名称:所有Foo方法在说$this->testPrivate时都会调用Foo::testPrivate,所有Bar方法都会调用Bar::testPrivate

总之,说"继承的方法被复制到孩子身上"是不正确的。实际发生的情况是,子级从用其父类的条目填充其方法名称查找表开始,然后添加自己的函数并替换任何重写的条目。当您调用$this->something时,会为当前对象的类查询此查找表。因此,如果$thisFoo的实例,并且Foo覆盖testPublic,则得到Foo::testPublic。如果$thisBar的一个实例,那么您将得到Bar::testPublic

好吧,private方法和属性正是私有的。对于所有意图和目的,您可以将它们视为"内部",即它们在其中定义的类的内部。这意味着它们永远不会被继承,也永远不会被重写。

因此,当将$thisprivate方法或属性组合使用时,它将始终是与对$this的引用在同一类中的方法或属性。发生这种情况是因为在父类中调用的$this无法访问另一个类中的private方法或属性(因为它们是私有的),甚至从子类访问。

希望这能有所帮助。