자바에서는 클래스 선언에 final 키워드를 사용해서 더이상 상속이 불가능하게 할 수 있다.
그러나 C++에서는 불가능하다.
문법적 트릭을 사용해서 해결해야 한다.
이번에 작업하면서 도구 클래스를 하나 만들었는데..
최소화된 기능의 도구인데다가 가상함수테이블을 생성시키지 않기 위해 virtual function을 하나도 만들지 않았다.
그래서 아래와 같이 소멸자도 가상화하지 않았다.
여기서 문제가 발생했다.
만약 사용하는 사람이 이 클래스를 상속해서 기능을 덧붙이고 사용한다면?
다음과 같은 문제가 발생할 수도 있다.
간단하게 소멸자를 가상 함수로 만들면 해결될 문제이지만..
몇가지 이유로 상속을 막고 최종 도구로만 사용하게끔 만들고 싶었다.
문제는 C++에서는 상속을 막을 수 있는 기능을 제공하지 않는다는 것이었다.
하지만 트릭은 있다.
상속한 자식 클래스는 부모 클래스의 기본 생성자를 이용하도록 되어있다는 점을 이용한 방법이다.
부모 클래스의 기본 생성자를 private으로 선언해 버리면 자식 클래스가 호출할 수 없으므로 컴파일 단계에서 상속이 차단된다.
하지만 위의 방법을 사용하면 기본 생성자가 없어서 생성이 조금 귀찮아진다.
그래서 C++의 창시자인 비야네 스트롭스트룹(Bjarne Stroustrup)씨는 아래와 같은 방법을 제안하고 있다.
원문 : http://www.research.att.com/~bs/bs_faq2.html#no-derivation
기발한 방법이긴 한데 이 방법은 코드가 상당히 지저분해진다.
그래서 아래와 같이 매크로로 정의해 보았다.
상속을 허락하고 싶지 않은 클래스를 만들 때 아래와 같이 매크로를 사용하면 된다.
코드가 상당히 깔끔해졌다.
위에 선언된 cTool 클래스를 상속하려고 들면 아래와 같이 컴파일 에러가 난다.
에러 메세지도 상속할 수 없다는 것을 알려주도록 깔끔하게 나온다.
문제 해결..

PS : 하지만 결국은 헤더 파일에 '이 클래스는 상속하면 안전하지 않습니다.'라고 문구를 넣는 것으로 결정.. ㅡ_ㅡ;
PS2: friend를 사용하지 않는 방법도 발견하였다.
출처 : http://www.ddj.com/cpp/184401394
이 방법은 friend를 사용하지 않기 때문에 매번 일일히 매크로를 사용할 필요없이 __NonDerivableClass 클래스만 어딘가에 선언해 두면 어떤 클래스에서건 private virtual로 상속받기만 하면 된다는 장점이 있다.
휠씬 간단하고 깔끔한 해결방법이 되겠다.
하지만 이 방법은 컴파일러가 아직 제대로 표준을 지원하지 못한 VC++ 6.0에서 제대로 작동하지 않는다는 단점이 있다.
VC2005를 확인해 본 결과 상속받지 못하게 컴파일 에러가 나며 정상적으로 작동하였다.
그러나 C++에서는 불가능하다.
문법적 트릭을 사용해서 해결해야 한다.
이번에 작업하면서 도구 클래스를 하나 만들었는데..
최소화된 기능의 도구인데다가 가상함수테이블을 생성시키지 않기 위해 virtual function을 하나도 만들지 않았다.
그래서 아래와 같이 소멸자도 가상화하지 않았다.
class cTool
{
public:
cTool();
~cTool();
public:
...
};
여기서 문제가 발생했다.
만약 사용하는 사람이 이 클래스를 상속해서 기능을 덧붙이고 사용한다면?
다음과 같은 문제가 발생할 수도 있다.
class cToolEx : public cTool
{
public:
cToolEx();
~cToolEx();
public:
...
};
cTool* getTool(void)
{
return (new cToolEx);
}
void main()
{
cTool* tool = getTool();
...
delete tool; // <-- cToolEx의 소멸자가 호출되지 않는다!!
}
간단하게 소멸자를 가상 함수로 만들면 해결될 문제이지만..
몇가지 이유로 상속을 막고 최종 도구로만 사용하게끔 만들고 싶었다.
문제는 C++에서는 상속을 막을 수 있는 기능을 제공하지 않는다는 것이었다.
하지만 트릭은 있다.
상속한 자식 클래스는 부모 클래스의 기본 생성자를 이용하도록 되어있다는 점을 이용한 방법이다.
부모 클래스의 기본 생성자를 private으로 선언해 버리면 자식 클래스가 호출할 수 없으므로 컴파일 단계에서 상속이 차단된다.
class cTool
{
private:
cTool(); // 기본 생성자를 제공하지 않는다.
public:
cTool(int n); // 다른 생성자를 제공
~cTool();
public:
...
};
class cToolEx : public cTool // <-- 컴파일 에러 발생!!
{
public:
...
};
하지만 위의 방법을 사용하면 기본 생성자가 없어서 생성이 조금 귀찮아진다.
new cTool; // <-- 이건 사용 못한다.
new cTool(0); // <-- 요렇게 사용해야 한다.
그래서 C++의 창시자인 비야네 스트롭스트룹(Bjarne Stroustrup)씨는 아래와 같은 방법을 제안하고 있다.
원문 : http://www.research.att.com/~bs/bs_faq2.html#no-derivation
class cTool;
class cNonDerivable
{
friend class cTool;
private:
cNonDerivable();
cNonDerivable(const cNonDerivable&);
};
class cTool : public virtual cNonDerivable
// 서로 friend이므로 private 멤버에 접근이 가능해서 상속이 가능하다.
// 그리고 virtual로 상속했으므로 friend의 자격을 자식 클래스에게 물려주지 않는다.
{
...
};
class cToolEx : public cTool
// 부모의 부모인 cNonDerivable 클래스의 기본 생성자에 접근할 수 없으므로 컴파일 에러!!
{
...
};
기발한 방법이긴 한데 이 방법은 코드가 상당히 지저분해진다.
그래서 아래와 같이 매크로로 정의해 보았다.
#define NonDerivableClass(x) \
class x; \
class __Cannot_Derive_Class_##x \
{ \
friend class x; \
private: \
__Cannot_Derive_Class_##x() {} \
__Cannot_Derive_Class_##x(const __Cannot_Derive_Class_##x&) {} \
}
#define NonDerivableSuper(x) public virtual __Cannot_Derive_Class_##x
상속을 허락하고 싶지 않은 클래스를 만들 때 아래와 같이 매크로를 사용하면 된다.
NonDerivableClass(cTool);
class cTool : NonDerivableSuper(cTool)
{
...
};
코드가 상당히 깔끔해졌다.
위에 선언된 cTool 클래스를 상속하려고 들면 아래와 같이 컴파일 에러가 난다.
class cToolEx : public cTool
{
...
};
D:\class_test.cpp(78) : error C2248: '__Cannot_Derive_Class_cTool::__Cannot_Derive_Class_cTool' : cannot access private member declared in class '__Cannot_Derive_Class_cTool'
D:\class_test.cpp(16) : see declaration of '__Cannot_Derive_Class_cTool::__Cannot_Derive_Class_cTool'
D:\class_test.cpp(16) : see declaration of '__Cannot_Derive_Class_cTool::__Cannot_Derive_Class_cTool'
에러 메세지도 상속할 수 없다는 것을 알려주도록 깔끔하게 나온다.
문제 해결..
PS : 하지만 결국은 헤더 파일에 '이 클래스는 상속하면 안전하지 않습니다.'라고 문구를 넣는 것으로 결정.. ㅡ_ㅡ;
PS2: friend를 사용하지 않는 방법도 발견하였다.
출처 : http://www.ddj.com/cpp/184401394
class __NonDerivableClass
{
protected:
__NonDerivableClass() {}
__NonDerivableClass(const __NonDerivableClass&) {}
};
class cTool : private virtual __NonDerivableClass
{
...
};
class cToolEx : public cTool
// 컴파일 에러
{
...
};
이 방법은 friend를 사용하지 않기 때문에 매번 일일히 매크로를 사용할 필요없이 __NonDerivableClass 클래스만 어딘가에 선언해 두면 어떤 클래스에서건 private virtual로 상속받기만 하면 된다는 장점이 있다.
휠씬 간단하고 깔끔한 해결방법이 되겠다.
하지만 이 방법은 컴파일러가 아직 제대로 표준을 지원하지 못한 VC++ 6.0에서 제대로 작동하지 않는다는 단점이 있다.
VC2005를 확인해 본 결과 상속받지 못하게 컴파일 에러가 나며 정상적으로 작동하였다.
'스킬북 > 코드 조각' 카테고리의 다른 글
| VC6과 VC8에서 wchar_t가 호환되게 만들기 (0) | 2009/10/16 |
|---|---|
| C++에서 상속이 안되는 클래스 만들기 (0) | 2009/10/16 |
| 자바스크립트로 만든 한글IME (0) | 2009/09/01 |
| 소스에서 유니코드 시스템 판단하기 (0) | 2007/11/28 |


|꼬마늑대|


댓글을 달아 주세요