[PHP OOP] 2. 생성자와 소멸자, final, 가시성 (Inheritance)

정현섭·2021년 6월 23일
1

PHP OOP

목록 보기
2/6
post-thumbnail

생성자와 소멸자

  • 생성자는 객체가 생성될때 호출되는 것
  • 소멸자는 객체가 소멸될때 호출되는 것

method 이름 앞에 __ 이 붙으면 magic method인데

__construct() , __destruct() 도 magic method의 일종이다.

Example 1 : 프로그램이 끝나면 객체 release

class A 
{
    public function __construct() {
        echo __METHOD__ . "\n";
    }

    public function __destruct() {
        echo __METHOD__ . "\n";
    }
}

$a = new A();
echo("hi\n");

이러면 출력되는 결과는 아래와 같다.

A::__construct
A::__destruct
hi

Example 2 : unset함수

<?php
class A 
{
    public function __construct() {
        echo __METHOD__ . "\n";
    }

    public function __destruct() {
        echo __METHOD__ . "\n";
    }
}

$a = new A();
unset($a);
echo("hi\n");

이러면 출력되는 결과는 아래와 같다.

A::__construct
A::__destruct
hi

unset함수를 실행했을 때 객체가 release되어서 소멸자가 호출된 것이다.

Example3 : ArgumentCountError

class A 
{
    public $message;
    public function __construct($message) {
        $this->message = $message;
    }
}

$a = new A();

위와 같은 코드를 실행시킬 경우,

$a = new A(); 부분에서 에러가 난다. (생성자에서 정의한 인자를 넣어주지 않아서.)

PHP Fatal error:  Uncaught ArgumentCountError: Too few arguments to function A::__construct()

Example 4 : 생성자, 소멸자 상속

<?php
class A 
{
    public function __construct() {
        echo __METHOD__ . "\n";
    }

    public function __destruct() {
        echo __METHOD__ . "\n";
    }
}

class B extends A 
{
}

$b = new B();

class B에서 따로 생성자, 소멸자 함수를 정의하지 않았다면, 객체가 생성되거나 소멸될 때 부모 클래스의 생성자 함수와 소멸자 함수가 실행된다.

위 코드의 결과는 다음과 같다.

A::__construct
A::__destruct

그리고 다른 엄격한 language에서와는 다르게 php는 자식 class에서 생성자 함수를 정의 할 경우, 부모의 생성자 함수가 실행되지 않는다.

그래서 자식 class에서 생성자를 정의(override)할 경우 코드 맨 윗 줄에 아래와 같이 명시적으로 부모 class의 생성자를 불러주는 것이 바람직하다.

class A 
{
    public function __construct() {
        echo __METHOD__ . "\n";
    }

    public function __destruct() {
        echo __METHOD__ . "\n";
    }
}

class B extends A 
{
    public function __construct() {
        parent::__construct();

        // code ...
    }
    public function __destruct() {
        parent::__destruct();

        // code ...
    }
}

Final

Final 키워드는 class 안의 method 혹은 class 자체에 사용 가능하다.

final은 말 그대로 거기서 끝이라는 뜻이다. 더이상의 상속이나 재정의가 불가능하도록 명시하는 키워드다.

method에 final을 쓸 경우

이 경우 해당 method를 자식 class에서 재정의(override)하지 못한다.

class A 
{
    public final function foo() {
        echo "foo~";
    }
}

class B extends A 
{
    public function foo() {
        echo "foofoo~~";
    }
}

그래서 위 코드는 에러가 난다.

(Cannot override final method A::foo())

class에 final을 쓸 경우

이 경우 해당 class자체를 상속받을 수 없게 된다.

final class A 
{
}

class B extends A 
{
}

그래서 위 코드는 에러가 난다.

(Class B may not inherit from final class (A))

Visibility (가시성)

class 내부의 visibility를 나타내는 키워드는 세가지다.

  • public
    • 아무나 접근 가능
  • protected
    • 해당 class나 자손 class에서만 접근 가능.
  • private
    • 해당 class에서만 접근 가능.

Example 1 : 여러 곳에서 접근 해보기

  • 바깥에서 접근할 경우.
<?php
class A 
{
    public $foo1 = 'foo1';
    protected $foo2 = 'foo2';
    private $foo3 = 'foo3'; 

    public function bar1() {
        echo "bar1\n";
    }
    protected function bar2() {
        echo "bar1\n";
    }
    private function bar3() {
        echo "bar1\n";
    }
}

$a = new A();

$a->foo1; // O 
$a->foo2; // X (error)
$a->foo3; // X (error)

$a->bar1(); // O
$a->bar2(); // X (error)
$a->bar3(); // X (error)
  • 자식 class에서 접근하는 경우
class A 
{
    public $foo1 = 'foo1';
    protected $foo2 = 'foo2';
    private $foo3 = 'foo3'; 

    public function bar1() {
        echo "bar1\n";
    }
    protected function bar2() {
        echo "bar1\n";
    }
    private function bar3() {
        echo "bar1\n";
    }
}

class B extends A
{
    public function test() {
        echo $this->foo1; // O
        echo $this->foo2; // O
        echo $this->foo3; // X (error)
    }
}

$b = new B();
$b->test();
  • 해당 class에서 접근하는 경우
class A 
{
    public $foo1 = 'foo1';
    protected $foo2 = 'foo2';
    private $foo3 = 'foo3'; 

    public function bar1() {
        echo "bar1\n";
    }
    protected function bar2() {
        echo "bar1\n";
    }
    private function bar3() {
        echo "bar1\n";
    }

    public function test() {
        echo $this->foo1; // O
        echo $this->foo2; // O
        echo $this->foo3; // O
    }
}

$a = new A();
$a->test();

Example 2 : private 생성자

class A 
{
    private $message = 'Hi';
    private function __construct() {
        echo $this->message . "\n";
    }
}

$a = new A(); // X (error)

위 코드의 경우 에러가 난다.

(바깥에서 new 로 객체를 생성했을 때 class A의 생성자 함수가 불리는데 그게 private이기 때문.)

class A 
{
    private $message = 'Hi';
    private function __construct() {
        echo $this->message . "\n";
    }

    public static function create() {
        return new self();
    }
}

$a = A::create();

위 코드의 경우 class A내부에서 생성자 함수를 불렀기 때문에 에러가 안나고 create 함수를 통해 객체를 받을 수 있다.

( 정상적으로 Hi 가 출력된다.)

위와 같은 구조를 활용하면 Singleton pattern (싱글턴 패턴) 을 만들 수 있다.

Example 3 : Singleton pattern

Singleton pattern이란 인스턴스를 하나만 만들어서 사용하는 패턴이다.

자원을 아낄 수 있고 인스턴스를 의도치 않게 여러 개 만드는 것을 방지한다.

class A 
{
    private static $instance;
    
    private function __construct() {
    }

    public static function create() {
        return self::$instance ? : self::$instance = new self();
    }
}

$a1 = A::create();
$a2 = A::create();

if($a1 === $a2)
    echo "same!";

위 코드는 최종적으로 "same"이 출력된다.

참고로 위 코드의 ?: 연산자는

foo == bar ?: baz; === foo == bar ? bar : baz; 다.

(stackoverflow)

1개의 댓글

comment-user-thumbnail
2023년 4월 19일

잘 보고 있습니다. 감사합니다.

답글 달기