developer tip

C ++에서 클래스 상속 방지

optionbox 2020. 12. 30. 08:05
반응형

C ++에서 클래스 상속 방지


최근에 한 친구가 C ++에서 클래스 상속을 방지하는 방법을 물었습니다. 그는 컴파일이 실패하기를 원했습니다.

나는 그것에 대해 생각하고 3 개의 답을 찾았다. 어느 것이 가장 좋은지 확실하지 않습니다.

1) 개인 생성자 (들)

class CBase
{

public:

 static CBase* CreateInstance() 
 { 
  CBase* b1 = new CBase();
  return b1;
 }

private:

 CBase() { }
 CBase(CBase3) { }
 CBase& operator=(CBase&) { }


};

2) CSealed 기본 클래스, 개인 ctor 및 가상 상속 사용

class CSealed
{

private:

 CSealed() {
 }

 friend class CBase;
};


class CBase : virtual CSealed
{

public:

 CBase() {
 }

};

3) CSealed 기본 클래스, 보호 된 ctor 및 가상 상속 사용

class CSealed
{

protected:

 CSealed() {
 }

};

class CBase : virtual CSealed
{

public:

 CBase() {
 }

};

위의 모든 메서드는 CBase 클래스가 더 이상 상속 될 수 없도록합니다. 내 질문은 :

1) 가장 좋은 방법은 무엇입니까? 다른 방법을 사용할 수 있습니까?

2) 방법 2와 3은 CSealed 클래스가 가상으로 상속되지 않는 한 작동하지 않습니다. 왜 그런 겁니까 ? vdisp ptr과 관련이 있습니까?

추신:

위 프로그램은 MS C ++ 컴파일러 (Visual Studio)로 컴파일되었습니다. 참조 : http://www.codeguru.com/forum/archive/index.php/t-321146.html


C ++ 11부터 클래스에 final 키워드를 추가 할 수 있습니다.

class CBase final
{
...

이 작업을 수행하고 싶어하는 주된 이유 (그리고이 질문을 찾는 이유)는 클래스를 하위 클래스가 아닌 것으로 표시하여 가상이 아닌 소멸자를 안전하게 사용하고 vtable을 모두 피할 수 있기 때문입니다.


상속을 방지 할 수 없습니다 (C ++ 11의 final키워드 이전에)-상속 된 클래스의 인스턴스화 만 방지 할 수 있습니다. 즉, 다음을 방지 할 방법이 없습니다.

class A { ... };

class B : public A { ... };

할 수있는 최선의 방법은 B 유형의 개체가 인스턴스화되지 않도록하는 것입니다. 그렇기 때문에 kts의 조언을 받아 A (또는 기타)가 상속에 사용되지 않는다는 사실을 문서화하고 비가 상 소멸자 만 제공하고 다른 가상 기능은 제공하지 않고 그대로 두는 것이 좋습니다.


더 이상의 하위 분류를 방지하기 위해 뒤틀림을 겪고 있습니다. 왜? 클래스가 확장 가능하지 않다는 사실을 문서화하고 dtor를 가상이 아닌 것으로 만듭니다. c의 정신에서 누군가가 당신이 의도 한 방식을 정말로 무시하고 싶다면 왜 그들을 막아야합니까? (나는 final자바에서 클래스 / 메소드 의 요점을 본 적이 없다 ).

//Note: this class is not designed to be extended. (Hence the non-virtual dtor)
struct DontExtened
{
  DontExtened();
  /*NOT VIRTUAL*/
  ~DontExtened();
  ...
};

1) 취향의 문제입니다. 내가 올바르게 본다면, 더 멋진 두 번째 및 세 번째 솔루션은 특정 상황에서 오류를 링크 시간에서 컴파일 시간으로 이동하므로 일반적으로 더 좋습니다.

2) 가상 상속은 (가상) 기본 클래스를 기본 클래스 ctor가 더 이상 도달 할 수없는 가장 많이 파생 된 클래스로 초기화하도록 강제하기 위해 필요합니다.


To answer your question, you can't inherit from CBase because in virtual inheritance a derived class would need to have direct access to the class from which it was inherited virtually. In this case, a class that would derive from CBase would need to have direct access to CSealed which it can't since the constructor is private.

Though I don't see the usefulness of it all (ie: stopping inheritance) you can generalize using templates (I don't think it compiles on all compilers but it does with MSVC)

template<class T>
class CSealed
{
    friend T;    // Don't do friend class T because it won't compile
    CSealed() {}
};

class CBase : private virtual CSealed<CBase>
{
};

If you can, I'd go for the first option (private constructor). The reason is that pretty much any experienced C++ programmer will see that at a glance and be able to recognize that you are trying to prevent subclassing.

There might be other more tricky methods to prevent subclassing, but in this case the simpler the better.


class myclass;

    class my_lock {
        friend class myclass;
    private:
        my_lock() {}
        my_lock(const my_lock&) {}
    };

    class myclass : public virtual my_lock {
        // ...
    public:
        myclass();
        myclass(char*);
        // ...
    };

    myclass m;

    class Der : public myclass { };

    Der dd;  // error Der::dd() cannot access
            // my_lock::my_lock(): private  member

I found it here to give credit. I am posting here just other people can easily access http://www.devx.com/tips/Tip/38482


To elaborate on Francis' answer: if class Bottom derives from class Middle, which virtually inherits from class Top, it is that most derived class (Bottom) that is responsible for constructing the virtually inherited base class (Top). Otherwise, in the multiple-inheritance/diamond-of-death scenario (where virtual inheritance is classically used), the compiler wouldn't know which of the two "middle" classes should construct the single base class. The Middle's constructor's call to the Top's constructor is therefore ignored when Middle is being constructed from Bottom:

class Top {
    public:
        Top() {}
}

class Middle: virtual public Top {
    public:
        Middle(): Top() {} // Top() is ignored if Middle constructed through Bottom()
}

class Bottom: public Middle {
    public:
        Bottom(): Middle(), Top() {}
}

So, in the the approach 2) or 3) in your question, Bottom() can't call Top() because it's inherited privately (by default, like in your code, but it's worth making it explicit) in Middle and thus is not visible in Bottom. (source)


One more solution:

template < class T >
class SealedBase
{
protected:
    SealedBase()
    {
    }
};

#define Sealed(_CLASS_NAME_) private virtual SealedBase<_CLASS_NAME_>


#include "Sealed.h"

class SomeClass : Sealed(Penguin)
{
};

ReferenceURL : https://stackoverflow.com/questions/2184133/prevent-class-inheritance-in-c

반응형