이번 글에서는 C++에서 클래스의 상속을 방지하는 방법에 대해 알아보겠습니다. 이처럼 더 이상 상속받지 못하도록 설계된 클래스를 final 클래스라고 부릅니다.
C++에는 final 문법이 없다?
Java나 C#에서는 final(또는 sealed) 키워드를 사용해 클래스의 상속을 손쉽게 차단할 수 있습니다. 하지만 C++에는 이를 위한 직접적인 언어 기능이 제공되지 않습니다. 그렇다면 어떻게 해야 할까요? 이번 글에서는 C++의 문법적 특성을 활용해 final 클래스를 흉내 내는(simulate) 기법을 살펴봅니다.
핵심 아이디어: MakeFinalClass 활용
먼저 MakeFinalClass라는 보조 클래스를 하나 만듭니다. 이 클래스의 기본 생성자를 private으로 선언하는 것이 핵심입니다. 외부에서 생성자에 접근할 수 없기 때문에, 이 클래스를 상속하는 파생 클래스는 객체를 생성할 수 없게 됩니다.
그리고 실제로 final처럼 동작시킬 MyClass를 MakeFinalClass의 friend 클래스로 지정합니다. friend 관계에 있는 클래스는 private 멤버에 접근할 수 있으므로, MyClass는 MakeFinalClass의 private 생성자를 호출할 수 있습니다.
왜 가상(virtual) 기반 클래스로 만들어야 할까?
주목해야 할 점은 MakeFinalClass를 가상 기반 클래스(virtual base class)로 만든다는 것입니다. 가상 기반 클래스의 생성자는 이를 직접 상속하는 중간 클래스가 아니라, 최종적으로 객체를 생성하는 구체 클래스(concrete class)의 생성자가 호출하기 때문입니다.
즉, 누군가 MyClass를 상속해 MyDerivedClass를 만들면, 컴파일러는 MyDerivedClass의 생성자 안에서 MakeFinalClass의 생성자를 호출하려고 시도합니다. 하지만 MakeFinalClass의 생성자는 private이고 MyDerivedClass는 friend가 아니므로 컴파일 에러가 발생하여 상속이 자연스럽게 차단됩니다.
예제 코드 – 상속 시도 시 컴파일 에러 발생
#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 자체는 정상적으로 사용 가능
반면 MyClass는 MakeFinalClass의 friend이므로 private 생성자에 접근할 수 있습니다. 따라서 MyClass의 객체는 아무 문제 없이 생성할 수 있습니다.
예제 코드 – 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++에서는 private 생성자 + friend 클래스 + 가상 상속의 조합을 통해 Java의 final 클래스와 유사한 효과를 낼 수 있습니다. 참고로 C++11부터는 class MyClass final { ... };처럼 final 지정자를 사용해 훨씬 간단하게 상속을 막을 수 있으므로, 최신 C++ 환경이라면 final 키워드를 사용하는 것이 좋습니다. 다만 이 기법은 구형 컴파일러 환경이나 final 지정자가 도입되기 전 코드베이스에서 여전히 유용하게 활용됩니다.