-1

在 php 手册中,他们将范围解析运算符定义如下:

范围解析运算符(也称为 Paamayim Nekudotayim)或更简单的术语是双冒号,是一个允许访问类的静态、常量和重写属性或方法的标记。

我的理解是,由于我们无法使用 评估静态属性、类常量和静态方法$this,因此我们需要::. 我不明白为什么::允许从类内部评估非静态函数。可以说,子类可能想用 评估父类中定义的方法parent::baseClassMethod(),但它可能也想评估父类中定义的属性,但::无法评估属性。可以说父类的属性是继承的,所以我们可以简单地用 来评估它们$this->prop,但方法也是如此。我们::仅在子类中重写方法时才使用 for 方法。同样我们需要::评估子类中的覆盖属性。与 php 手动定义相反,如果您尝试使用 评估覆盖的属性::,则会引发错误。

为了说明我的观点,我有以下示例 PHP 代码:

error_reporting(E_ALL);
class myClass {
    private $prop = 786; 
    public $prop2 = 123;

    public function changeType($var, $type){
        settype($var, $type);
        echo "function assessed through self";
    }
    public function display_prop(){
        self::changeType(1, "string"); //why does this not throw error for non static function?
        var_dump(self::$prop); //throws error; can't assess properties with self as expected.
    }    
}

class childCLass extends myClass {
    public $prop2 = "new"; //overriden property.

    public function display_prop(){ //overriden method.
        echo "I do different things from the base class". "</br>";      
    }
    public function dsiplay_overriden(){
        echo parent::$prop2; //Why can't assess overriden properties, as suggested in the definition?
    }
}

$obj = new myClass;
$obj->display_prop(); 

$obj2 = new childCLass;
$obj2->display_prop();
$obj2->dsiplay_overriden();

childClass::display_prop(); //This throws error as expected because non-static method.

总结一下,我主要有两个具体问题:

  1. 为什么我们不能访问::定义中定义的覆盖属性?
  2. ::与定义相反,为什么我们可以访问类中的非静态函数?

PS:在stackoverflow上也提出了类似的问题。不存在令人满意的答案,而且我正在寻找更适合programmers.stackexchange 的概念性和有见地的答案。

4

1 回答 1

2

我会试一试:)

方法和静态属性在内存中只存在一次,类实例(对象)的非静态属性每个实例都存在,例如。有自己的记忆空间。

  1. 您不能“覆盖”类的公共或受保护属性。相反,派生类中相同属性名的声明将“掩盖”基类声明。实际上,它们在内存中共享相同的位置,使它们“相同”,就像您在全局命名空间中两次声明同名变量一样。这样做的原因是继承,您继承了基类的所有公共和受保护属性。私有属性不会被继承,派生类的实例将有 2 个单独的内存区域用于同名属性。请参见下面的示例 A。

  2. 范围解析不禁止非静态访问。PHP 文档中的第一句话甚至是这样说的:The Scope Resolution Operator (also called Paamayim Nekudotayim) or in simpler terms, the double colon, is a token that allows access to static, constant, and overridden properties or methods of a class. 事实上,它解决了范围,例如。它确定是否必须访问静态(共享)或本地内存。

特殊变量$this只能访问本地内存,它代表“对当前实例的引用”,并且在非静态类方法中自动可用,但在静态方法中不可用!从历史上看,它来自 C++(关键字“this”),其中类方法被组织在所有类方法的函数指针映射中,即所谓的 v-table。所有非静态类方法都会收到一个名为“this”的附加(不可见)最后一个参数,并且在运行时解析对非静态方法的调用(与在编译时解析的静态方法相反)。

现在,当您创建类的实例时,$obj = new Foo();会发生两件事 - 为对象分配内存并调用类的构造函数。构造函数不能被声明为静态的,仅仅因为它们负责正确地初始化内存。作为非静态方法,它们会$this自动接收。

因此,当调用一个非静态方法时,例如$obj->method(),运行时环境会识别,$obj是一个类实例。它检查类的 v-table 以找到方法(这只不过是一个常规函数,但带有额外的参数 $this)并调用它,传递$objas$this伪代码$vtable['Foo']['method']($obj)。在类方法中,您可以访问 $this。

最后,静态调用非静态方法,分别以非静态方式调用静态方法:实际上,如果您牢记以下内容,您可以同时执行这两种方法。

Foo::bar();使用:调用的非静态方法$this将是null!

静态方法,调用方式$obj->bar();:不能$this在方法中引用,因为它不会自动传递!

示例 A(公共、受保护和私有成员的继承以及静态/非静态方法访问):

class A {
    public $x = 'a_x';
    protected $y = 'a_y';
    private $z = 'a_z';
}
class B extends A {
    public $x = 'b_x';
    protected $y = 'b_y';
    private $z = 'b_z';

    public static function foo() {
        echo 'foo' . PHP_EOL;
    }
    public function bar() {
        echo 'bar' . PHP_EOL;
    }
}
$a = new A();
$b = new B();

var_dump($a, $b);
$b->foo();
B::bar();

输出:

object(A)#1 (3) {
  ["x"]=>
  string(3) "a_x"
  ["y":protected]=>
  string(3) "a_y"
  ["z":"A":private]=>
  string(3) "a_z"
}
object(B)#2 (4) {
  ["x"]=>
  string(3) "b_x"
  ["y":protected]=>
  string(3) "b_y"
  ["z":"B":private]=>
  string(3) "b_z"
  ["z":"A":private]=>
  string(3) "a_z"
}
foo
bar

如您所见,使用 调用静态函数$this->foo()没有问题,使用 调用非静态函数也没有问题B::bar()。但是,如果您尝试$this在这些情况下访问,您将收到致命错误...

于 2020-03-11T18:57:32.990 回答