最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • PHP类的秘密(五) 构造函数详解

    正文概述 转载于:掘金(传达室老大爷)   2021-06-25   315

    在类的秘密第一篇的时候, 我们提到为属性赋初始值的例子. 类的每个实例都可能会有不同的初始值, 是不是可以在创建实例的时候, 就一起为属性赋值呢?

    当然可以, 这就是今天要讲到的构造函数.

    基础知识

    构造函数是一种特殊的成员方法, 用来为创建对象时, 初始化对象属性. 创建构造函数时, 注意是两条下划线:

    function __construct($parameters,....) {statements}

    它的基本规则:

    1. 它是在每次创建对象时自动执行的, 可以为成员变量赋初值, 也可以执行一些其他初始化的操作, 比如打印欢迎语.

    2. php规定, 每个类只能有一个构造函数.

    3. 因为其意义, 构造函数不能定义为静态static, 所以不能在类外直接调用, 但可以被其他成员方法调用.

    我们看一个典型的构造函数:

    class Sport2    
    {
        public ? string $name;
        public $sex;
        public ? $weight;
        function __construct(? string $name, protected $age, ? int $weight, $sex='male')
        {
            $this->name=$name;
            $this->sex=$sex;
            $this->age=$age;
            $this->weight=$weight;
            echo '对象初始化完成';
        }
        function showMe() 
        {
            self::__construct('test',5,10); //可以通过其他成员方法调用构造函数;
        }
    }
    $player2=new Test1('Snow',35, 88); //声明对象的同时,自动调用构造函数,赋予初始值
    $player2->shouMe();
    

    通过上例, 我们可以得出构造函数的几个用法:

    1. 定义属性时, 我们可以在属性前面的问号 ? , 自 PHP 7.1.0 起,类型声明允许前置一个问号 (?) 用来声明这个值允许为指定类型,或者为 null。

    2. 构造函数中可以给属性赋默认值, 可以指定属性数据类型, 可以新增属性.

    3. 还可以给新增的属性指定可见度, 但如果原属性已经有可见度了, 就不能再改了.

    4. 创建对象时, 将构造器参数放在类名后的括号里调用。

    5. 其他成员方法可以调用构造函数.

    构造函数的继承

    我们通过实例, 看一下子类的构造函数和父类构造函数的关系:

    class BaseClass {                    
        function __construct(protected $a, int $b)
        {  //父类构造函数, 带两个必选参数
            print "BaseClass\n";
        }
    }
    
    class SubClass extends BaseClass {
        function __construct($a)         //子类构造函数, 只引用一个父类属性
        { 
            parent::__construct(5,5);  //调用父类构造函数
            print "SubClass\n";
        }
    }
    
    class OtherSubClass extends BaseClass {
        // 子类, 无自己的构造函数
    }
    
    $obj1 = new SubClass();     //BaseClassSubClass
    $obj2 = new OtherSubClass(5,9); //BaseClass
    

    从上例, 我们可以看到:

    1. 当子类对象被创建时, PHP 会优先使用子类自己的构造函数.

    2. 如果子类中没有自己的构造方法,则 PHP 会调用父类中的构造方法.

    3. 子类构造方法可以设置任意参数和方法体, 不受父类构造函数限制. 这一点和我们之前讲过的重写不一样. 可以理解为, 子类和父类的构造函数是独立的.

    如何在一个类中实现多个构造函数?

    我们之前提到每个 class 只能有一个构造器。 然而在某些情况下,需要针对不同的情况实现不同的方式构造对象。除了通过子类以外, 我们还可以通过static静态方法.

    举个例子:

    class Product {              
    private ?int $id;              //数据类型前面加?,表示要么是int,要么是null
    private ?string $name;
    
    //设置构造器为 private 或 protected,防止自行额外调用。
    private function __construct(?int $id = null, ?string $name = null) 
    {
        $this->id = $id;
        $this->name = $name;
    }
    
    public static function fromBasicData(int $id, string $name)
    {     
        $new = new static($id, $name);   //将用户输入的变量带入构造函数, 并新建对象;
        return $new;             //返回新建对象;
    }                                       //static指代调用对象的类;
    
    public static function fromArray(array $arr)  {
        $data = $arr;
        return new static($data['id'], $data['name']);   //将数组元素传入构造函数
    }
    
    public static function fromString(string $stringvar){
        $data = explode('@',$stringvar);       //将字符串转换为数组
        $new = new static();                  //构造函数的参数可以为null
        $new->id = $data['id'];               //再进行属性赋值;
        $new->name = $data['name'];
        return $new;
    }
    }
    $p1 = Product::fromBasicData(5, 'Widget');   //调用静态方法, 直接赋值;
    $p2 = Product::fromArray($arr);               //从数组中为对象赋值
    $p3 = Product::fromString($stringvar);          //从字符串中为对象赋值
    

    上面这个例子中, 有2个很重要的点:

    $new = new static($id, $name)

    这个语句, 实际上是在类内部调用构造函数. 其中static在这里是代词, 指代调用它的类, 在上面的例子中, 就是Product::. 那么 new static (id,id, id,name) 等价于 new Product(id,id, id,name). 然后将新建的对象赋值给$new, 作为函数的返回值, 赋值给$p1. 后面两个例子都是一个意思. PHP管它叫"后期静态绑定", 不用太纠结这个概念.

    第二个点, 这里将构造函数__contruct()设置为了private, 这样就无法在类外通过new来创建对象, 会导致报错, 只能通过同类的static静态方法进行调用. 这里大家可以思考一下, 如果删掉构造函数__contruct()是不是可以?

    其实也是可以的, 我们可以对上面的例子进行适当改写, 一样可以达到通过调用不同函数创建按不累同类对象的目的. 但是呢, 就无法限制在类外直接new一个新对象了.

    析构函数 __destruct()

    最后我们再来看看析构函数, 我们既然可以创建对象, 当然也可以销毁一个对象. 这就用到了析构函数, 析构函数往往用来做"清理善后" 的工作,释放内存.

    function __destruct()    //是双下划线
    {
        表达式;
    }
    

    析构函数的规则:

    1. 析构函数会在到某个对象的所有引用都被删除, 或者当对象被显式销毁时, 系统会自动执行;

    2. 析构函数必须是public, 否则系统会报错;

    3. 子类可以定义自己的析构函数, PHP会优先执行子类的析构函数, 如果子类没有定义析构函数则会继承父类的。

    4. 子类要执行父类的析构函数,必须在子类的析构函数体中显式调用parent::__destruct()

    我们来验证一下析构函数的规则:

    class Foo4 {
    
    private $name;   
    private $link;   
    
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function setLink($link){   
        $this->link = $link;
    }
    
    public function __destruct() {
        echo 'Destroying: ', $this->name, PHP_EOL;
    }
    

    }

    $foo = new Foo4('Foo 1');
    $bar = new Foo4('Foo 2');
    $foo->setLink($bar);   //让两个对象互相引用
    $bar->setLink($foo);
    $foo1 = null;         //将两个变量重新赋值;
    $bar1 = null;
    
    $foo1 = new Foo4('Foo 3');
    $bar1 = new Foo4('Foo 4');
    
    echo 'End of script', PHP_EOL; 
    
    //output:
    Destroying: Foo 3
    Destroying: Foo 4
    End of script
    Destroying: Foo 1
    Destroying: Foo 2
    

    可以从输出结果看到, 互为引用的foofoo和foo和bar在程序都运行完毕后才被清除. 而foo3和foo4在创建完后就被清除了. 原因在于, foofoo和foo和bar互为引用, 即便之后他们被重新赋值, 但是他们的引用关系依然保存在了系统内存当中. 这会导致系统无法调用析构函数销毁他们, 直到整个程序结束.

    最后, 我们总结一下:

    1. 我们讲了构造函数的基本规则, 并举例了它的不同用法.
    2. 构造函数的继承特点, 和普通成员方法不一样.
    3. 以及如何通过static这个代词来包装构造函数, 以实现不同输入格式的对象
    4. 最后是析构函数, 以及它的内在机制.

    总结不易, 请勿私自转载, 否则别怪老大爷不客气!

    参考资料:

    www.php.net PHP官方文档

    零基础PHP学习笔记 明日科技


    起源地下载网 » PHP类的秘密(五) 构造函数详解

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元