본문 바로가기
  • 쓸쓸한 개발자의 공부방
C++

[C++] 디자인패턴 : protected constructor

by 심찬 2022. 1. 27.

C++ 디자인 패턴 :

protected constructor (protected생성자)

 

생성자의 정확한 호출 순서를 이해해야 한다.

1. Dog의 생성자가 먼저 호출된다.  (Dog   d; 에 의해 호출됨)

2. Dog의 생성자 안에서 기반 클래스인 Animal의 생성자를 호출한다.

class Animal
{
protected:
    Animal() {}
};

class Dog : public Animal
{
public:
    Dog() {} // Dog() : Animal() {}
};

int main()
{
    Animal a;   // error
    Dog    d;   // ok.
}

생성자를 protected에 만드는 이유?

- 자기 자신(Animal)을 만들 수는 없다.

- 하지만 파생 클래스(Dog)의 객체는 만들 수 있다.

- 동물은 추상적 개념으로 객체가 존재할 수 없지만 강아지는 현실 세계에 객체가 존재하는 이치와 같다.

 

 

protected 소멸자

 

1. 객체를 스택에 만들 수 없게 할 때 주로 사용하는 기업

2. 객체를 힙에만 만들 수 있게 한다.

3. 참조 계수 기반의 객체 수명 관리 기법에서 주로 사용한다.

 

 

아래와 같이 protected 안에 ~Car() 소멸자를 만들면 아래와 같은 에러가 발생한다.

소멸자가 자동으로 호출되지만 protected에 의해 오류가 발생하는 것이다.

#include <iostream>
using namespace std;

class Car
{
public:
    Car() {}

    void Destroy() { delete this;}
protected:
    ~Car(){ cout << "~Car" << endl;}
};

int main()
{
    Car c; // 스택에 객체를 만들수 없다.
}

 

에러 문구

main.cpp:16:9: error: ‘Car::~Car()’ is protected within this context
   16 |     Car c; // 스택에 객체를 만들수 없다.
      |         ^
main.cpp:11:5: note: declared protected here
   11 |     ~Car(){ cout << "~Car" << endl;}
      |     ^

 

 

그래서 Car를 포인터 형태 객체로 생성할 수 있다.

하지만 아래와 같이 delete p; 를 수행하면 역시나 에러가 발생한다.

소멸자가 역시나 protected 안에 존재하기 때문이다.

#include <iostream>
using namespace std;

class Car
{
public:
    Car() {}
protected:
    ~Car(){ cout << "~Car" << endl;}
};

int main()
{
    Car* p = new Car;
    delete p;
}

역시 protected에 의해 에러가 발생한다.

 

 

그래서 아래와 같이

Destroy()를 만들어주고 멤버함수를 통해 p->Destroy(); 호출해주면 문제없이 컴파일이 되는 것을 볼 수 있다.

Destroy()는 자기 자신을 delete 하기 때문에 문제없이 protected 접근이 가능하고 잘 수행된다.

#include <iostream>
using namespace std;

class Car
{
public:
    Car() {}

    void Destroy() { delete this;}
protected:
    ~Car(){ cout << "~Car" << endl;}
};

int main()
{
    Car* p = new Car;
    p->Destroy();
}

 

 

자주 사용하는 기법은 아니지만 숙지할 것!

나중에 참조 계수 기반의 객체 수명 관리 기법 공부할 때 다시 한 번 숙지!

댓글