Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++에서 final 클래스 시뮬레이션하기: 상속 불가 클래스 만드는 방법

final 클래스란?

Java나 C#에서는 final 클래스를 사용할 수 있습니다. final 클래스는 더 이상 다른 클래스가 상속(확장)할 수 없도록 봉인된 특수한 형태의 클래스입니다. 하지만 C++에는 이런 기능을 직접 지원하는 문법이 없기 때문에, 몇 가지 기법을 조합해 final 클래스의 동작을 흉내 내야 합니다.

시뮬레이션의 핵심 원리

아이디어는 간단합니다. MakeFinalClass라는 보조 클래스를 하나 추가로 만들고, 이 클래스의 기본 생성자를 private으로 선언하는 것입니다. 생성자가 private이면 외부 코드에서는 이 클래스의 객체를 만들 수 없습니다.

그다음, 봉인하고 싶은 클래스인 MyClass가 MakeFinalClass를 상속받되, 두 클래스를 friend 관계로 연결합니다. friend 클래스는 private 멤버에 접근할 수 있는 특권이 있으므로, 결과적으로 MyClass만이 MakeFinalClass의 생성자를 호출해 정상적으로 객체를 생성할 수 있게 됩니다.

왜 가상 베이스 클래스(virtual base class)여야 할까?

여기서 주목해야 할 점은 MakeFinalClass를 가상 베이스 클래스로 상속한다는 사실입니다. 가상 베이스 클래스의 생성자는 이를 직접 상속한 클래스(MyClass)가 아니라, 실제 객체를 생성하는 최종 구체 클래스(MyDerivedClass)의 생성자가 호출합니다. 따라서 MyDerivedClass가 인스턴스를 만들려는 순간 MakeFinalClass의 private 생성자 호출이 시도되고, 접근 권한 위반으로 컴파일 에러가 발생해 상속이 사실상 차단됩니다.

예제 1: 상속을 시도하면 컴파일 오류 발생

#include <iostream>
using namespace std;
class MyClass;
class MakeFinalClass {
private:
    MakeFinalClass() {
        cout << "This is constructor of the MakeFinalClass" << endl;
    }
    friend class MyClass;
};
class MyClass : virtual MakeFinalClass {   // 이 클래스가 final 클래스가 됨
public:
    MyClass() {
        cout << "This is constructor of the final Class" << endl;
    }
};
// 파생 클래스 생성 시도
class MyDerivedClass : MyClass {
public:
    MyDerivedClass() {
        cout << "Constructor of the Derived Class" << endl;
    }
};
main() {
    MyDerivedClass derived;
}

실행 결과

In constructor 'MyDerivedClass::MyDerivedClass()':
[Error] 'MakeFinalClass::MakeFinalClass()' is private

MyDerivedClass의 객체를 생성하려면 가상 베이스 클래스인 MakeFinalClass의 생성자가 먼저 호출되어야 하는데, 이 생성자는 private이므로 컴파일러가 에러를 출력합니다. 즉, MyClass를 상속하는 새로운 클래스는 만들 수 없습니다.

예제 2: MyClass 객체 생성은 정상적으로 동작

반면 MyClass 자체는 MakeFinalClass의 friend이므로 private 생성자에 접근할 수 있습니다. 따라서 MyClass 타입의 객체는 아무 문제 없이 생성할 수 있습니다.

#include <iostream>
using namespace std;
class MyClass;
class MakeFinalClass {
private:
    MakeFinalClass() {
        cout << "This is constructor of the MakeFinalClass" << endl;
    }
    friend class MyClass;
};
class MyClass : virtual MakeFinalClass {   // 이 클래스가 final 클래스가 됨
public:
    MyClass() {
        cout << "This is constructor of the final Class" << endl;
    }
};
main() {
    MyClass obj;
}

실행 결과

This is constructor of the MakeFinalClass
This is constructor of the final Class

참고: C++11 이상이라면 final 키워드를 사용하세요

위에서 소개한 기법은 C++11 이전 환경에서 널리 쓰이던 전통적인 방식입니다. C++11부터는 표준 언어 차원에서 class MyClass final { /* ... */ };처럼 final 키워드를 직접 지원하므로, 최신 컴파일러를 사용하는 프로젝트라면 복잡한 우회 기법 없이도 한 줄로 상속을 완벽하게 차단할 수 있습니다.