Corgi Dog Bark

ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [c++] 상속을 어떻게 멈출 수 있을까 ?
    뜯고 또 뜯어보는 컴퓨터/씨쁠쁠 C++ 2022. 11. 6. 00:24
    반응형

    질문.


    마침 이런 생각을 할 수 있겠다. 상속되는 자식 객체에게 더 이상의 자식 객체를 만드는 것을 허락하지 않을 수 있을까? 이번 글에서는 이러한 객체를 만드는 디자인적인 방법론과 C++에서 제시하는 방법에 대해 알아보겠습니다.

     


     

    정답 공개

    더보기

    1. Final 키워드를 사용한다.

    2. Constructor를 private으로 사용한 다음, static 함수를 사용하여, 객체를 얻어온다.

    3. private 생성자를 가진 dummy class를 활용하여, friend 키워드 활용하기.

     

     

    1. final keyword 사용


    c++ class 객체에서 final 키워드를 사용하게 된다면, 더 이상 클래스를 상속하는 것을 허락하지 않습니다. 따라서 어떠한 클래스도 final 클래스를 사용한 객체에 대해 상속받을 수 없게 됩니다. 만약 final 키워드를 사용한 객체를 상속받으려 한다면, 컴파일러는 에러를 내뿜게 됩니다.

     

    c++에서 final 클래스는 클래스 내부 멤버 함수 그리고 클래스 자체에도 사용이 가능합니다. 여기서 사용하는 예제는 클래스 자체에 사용하여, 더 이상 이 클래스를 상속받을 수 없도록 할 것입니다. (final과 override 키워드에 대해서는 추후 글에서 정리할 수 있도록 하겠습니다.)

     

    본 예제에서는 다음과 같이 사용하였습니다.

    #include <iostream>
    
    using namespace std;
    
    class Unique final{
    public:
    	Unique(){
    		cout << "final Unique" << endl;
    	}
    };
    
    class Derived : public Unique {
    public:
    	Derived() {
    		cout << "Derived Class " << endl;
    	}
    };
    
    int main(){
    	Unique u;
    	Derived d;
    }

    [그림 1. 에러를 출력하는 메시지]

     

     

    2. private 생성자 사용


    유니크한 객체를 만들기 위해서, 싱글톤을 유지하는 방법과 비슷한 방법을 사용할 것입니다. 생성자를 private 하게 만든 다음, GetInstance( ) 객체를 통해서만 객체를 얻어 올 수 있도록 할 것입니다. GetInstance( ) 함수를 사용하여, 객체를 생성하고, 사용자에게 객체를 전달해줄 것입니다.

     

    이를 사용하는 이유는 상속된 객체는 부모 객체의 생성자를 생성후, 자식 객체의 생성자를 생성하게 되는 원리를 이용하여, 자식 객체를 상속하는 객체는 더 이상 생성자를 호출하지 못하도록 하는데 그 원리가 있습니다. 따라서 private 생성자를 가진 객체를 상속하려는 객체는 더 이상 컴파일 타임 오류를 내뿜으며, 상속된 객체를 사용할 수 없다고 할 것입니다.

     

    그럼 예시를 들어보겠습니다.

    #include <iostream>
    
    using namespace std;
    
    class Unique {
    private:
      Unique() { cout << "final Unique" << endl; }
    
    public:
      // 객체를 반환하는 함수
      static Unique *GetInstance() { return new Unique(); }
    };
    
    class Derived : public Unique {
    public:
      Derived() { cout << "Derived Class " << endl; }
    };
    
    int main() {
      Unique* u = Unique::GetInstance();
      delete u;
      Derived d;
    }

    [그림 2. 에러를 출력하는 메시지]

     

     

    3. private 생성자를 가진 Dummy Class 활용하기


    가상 상속(virtual Inheritance)이라는 개념을 활용해서, private 한 생성자 생성을 금지시키는 원리입니다. 가상 상속(virtual Inheritance)은 본디 다이아몬드 구조 상속에서 자주 설명하는 기법인데, virtual Inheritance에 대해서는 추후에 설명을 하도록 하겠습니다.

     

    원리는 간단합니다. dummy class 가 private 한 생성자를 가지도록 설정해주고, private 한 생성자를 가진 dummy class 를 virtual 상속을 받는 Unique 클래스를 설정해줍니다. 그리고, 이러한 Unique 클래스와 dummy class는 friend 관계를 맺을 수 있도록 합니다. (Unique 클래스가 private 생성자를 호출할 수 있게끔 하기 위해서입니다.) 이제 설정은 끝나게 되었는데, Unique 클래스는 더 이상 상속을 할 수 없습니다...

     

    이때 원리는 간단합니다.

    - virtual 상속 시, 최상위 가상 base 클래스는 제일 밑의 class 가 생성하게 됩니다. 따라서 밑에서 보게 되었을 시, Unique 클래스를 상속받은 Derived 클래스는 dummy 클래스를 호출하게 되고, 이때 dummy 클래스는 private 생성자로 Derived가 호출을 할 수 없습니다. 따라서 생성이 불가능합니다.

    - 또한 Unique 생성자는 dummy 클래스를 생성 가능한데, 이는 friend 키워드로 private 생성자에 접근이 가능하기 때문입니다.

    - 이때 왜 Unique에 가상 상속(virtual Inheritance)을 시켰나 의문이 드시는 분들도 계실 텐데, 이는 Derived 클래스가 dummy 클래스의 생성자를 호출하려 하려고 의도했기 때문입니다. 만약 public dummy로 상속을 시키면 Unique 클래스를 상속받은 Derived 클래스를 생성 가능하게 되는데, (즉 원하는 바와 다르게 동작합니다.) 이는 Unique 객체가 dummy 클래스를 생성하게 되어, Derived는 dummy 객체가 private 인지 뭔지 알지 않아도 되기 때문입니다.

    #include <iostream>
    
    using namespace std;
    
    // dummy class
    class dummy {
    private:
      dummy() {}
      friend class Unique;
    };
    
    class Unique : virtual dummy {
    public:
      Unique() { cout << "final Unique" << endl; }
    };
    
    class Derived : public Unique {
    public:
      Derived() { cout << "Derived Class " << endl; }
    };
    
    int main() {
      Unique u;
      Derived d;
    }

     

    이렇게 상속을 금지하도록 하는 방법 3가지를 알아보았는데, 

    질문이 있으시거나, 궁금하신점은 댓글 달아주시길 바랍니다!

     

    긴 글 읽어주셔서 감사합니다.

    반응형

    댓글

Designed by Tistory.