欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

php为什么先执行后实例化的对象的析构函数

程序员文章站 2022-05-04 14:35:35
...
问题1:问题如题,自己做了测试
class Obj{        
public $i;        
        public function construct($t){            
        $this->i = $t;            
        echo "执行构造函数$this->i";            
        echo "<br>";
        }        
        public function destruct(){            
        echo "执行析构函数$this->i";            
        echo "<br>";
        }
    }
    $obj1 = new Obj(1);
    $obj2 = new Obj(2);

执行构造函数1执行构造函数2执行析构函数2执行析构函数1

问题2:在子类中调用父类的构造方法是否只是对父类进行初始化,是否产生父类的对象?

======================================UPDATE======================================

找到一段理解比较深刻说法:

使用堆还是栈来存储数据是由PHP引擎决定的,PHP开发者不需要关心.
转:
在PHP5的Zend Engine的实现中,所有的值都是在堆上分配空间,并且通过引用计数和垃圾收集来管理.
PHP5的Zend Engine主要使用指向zval结构的指针来操作值,在很多地方甚至通过zval的二级指针来操作.
而在PHP7的Zend Engine实现中,值是通过zval结构本身来操作(非指针).
新的zval结构直接被存放在VM的栈上,HashTable的桶里,以及属性槽里.
这样大大减少了在堆上分配和释放内存的操作,还避免了对简单值的引用计数和垃圾收集.

======================================UPDATE1======================================

找到了具体说明的地方

$p1 = new Person();对于这个条代码,$p1 是对象名称在栈内存里面new Person()是真正的对象是在堆内存 里面的,具体的请看下图:

php为什么先执行后实例化的对象的析构函数

这样就解释了为什么先实例化的对象是后释放的

new Person();实际返回的是一个对象的引用,然后引用赋值给$p1,$p1是存储在栈中的变量,是一个指针,指向该对象在堆中分配的实体

这同时也解释了php底层存储变量是有一个hash符号表来维护变量的生命周期的,符号表中存有key=>value键值对,key为变量名称,key指向zval结构体,即value的首地址

构造函数和析构函数的执行事实上使用的是一个 栈 结构,由于 Obj(2) 是在后面创建的,因此位于栈顶的位置,按照栈 先进后出 的顺序,销毁时,Obj(2) 就是先被销毁了。

看完楼主的题当时也是好多疑问,我也想知道为什么不是

执行构造函数1
执行构造函数1
执行析构函数2
执行析构函数2

PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行

也就是流程是这样的

Obj(1) 启动,申请自己的内存空间与上下文环境
Obj(2) 启动,申请自己的内存空间与上下文环境
Obj(2) 销毁,垃圾回收
Obj(1) 销毁,垃圾回收

就是Obj(1) 先进后出一样,他是后实例化,所以先销毁

----------------------------
Obj(1) Obj(2) Obj(2) Obj(1)
----------------------------

问题2:在子类中调用父类的构造方法是否只是对父类进行初始化,是否产生父类的对象?

同样手册。继承已为大家所熟知的一个程序设计特性,PHP 的对象模型也使用了继承。继承将会影响到类与类,对象与对象之间的关系。

比如,当扩展一个类,子类就会继承父类所有公有的和受保护的方法。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能。

子类可以调用父类方法,继承关系不存在实例化

我的想法:

问题1:持有对象引用的变量是存放在栈里面的,栈是先进后出,变量obj2先与obj1销毁

问题2:只会产生一个子类的对象

第一个问题:obj1和obj2很显然都是存放在栈内存中,根据栈内存的特点先进后出,销毁的时候自然是obj2先销毁,也就是obj2的destruct先执行,然后才是obj1销毁,即执行obj1的_desctruct。这也就解释了你的顺序问题。

第二个问题:不会产生父类对象,当你实例化一个子类的时候,父类的公有和受保护的方法会在实例化的对象上。因此你就可以调用父类的方法。

以上就是php为什么先执行后实例化的对象的析构函数的详细内容,更多请关注其它相关文章!