Подтвердить что ты не робот

Как запретить публичное наследование, но разрешить частное (и защищенное) наследование

В С++ 11 было введено ключевое слово final, чтобы запретить будущие переопределения или запретить наследование. Наиболее распространенный пример, где он может использоваться, относится к классам, которые не предназначены для использования в качестве базовых классов (например, не виртуальных деструкторов). Тем не менее, иногда мы можем захотеть иметь отношение is-реализованное-в-термины между двумя классами (т.е. private inheritance), а не is-a отношение (public наследование). Однако final запрещает оба типа наследования.

Мой вопрос заключается в следующем: существует ли способ разрешить наследование private, но запрещает наследование public (возможно, не напрямую, но по крайней мере мы можем "имитировать" его)? В этом случае проблем не будет, даже если мы будем использовать класс с не виртуальным деструктором, так как мы не можем напрямую использовать производный класс с помощью указателя на базу, поэтому мы должны быть в порядке.

Я думаю о таком коде:

class Base /*final*/ {}; // making it final prohibits both private and public inheritance

class PrivateDerived: private Base{}; // this should work

class PublicDerived: public Base{}; // this shouldn't

int main()
{
    PrivateDerived prvd;
    PublicDerived  pubd; // this should not compile

    // Base* pBase = new PrivateDerived; // doesn't work, so we are ok
}
4b9b3361

Ответ 1

Интересный вопрос! Если вы не возражаете отказаться от тривиальности деструктора, я думаю, что следующее выполняет эту работу:

#include <type_traits>

template <typename T>
class Base {
protected:
    ~Base() {
        static_assert(!std::is_convertible<T*,Base*>::value, "Invalid use of public inheritance.");
    }    
};

class Derived : public Base<Derived> {
};

int main() {
    Derived d;
}

Не удалось скомпилировать следующий код: static_assert срабатывает, потому что Derived* конвертируется в Base<Derived>*. Однако, если вы измените наследование на protected или private, тогда компиляция кода.

К сожалению, пользователи все еще могут стрелять в ногу:

class Bad : public Base<Derived> {
};

Ответ 2

Я не уверен, что это то, что вы ищете, или если это поможет вам в вашем случае. Однако я продемонстрирую полиморфное поведение.

Защищенный конструктор Абстрактный класс

class BaseProtected {
// ----- Member Variable Section -----
public: 
    // There Shouldn't Be Public Variables In A Base Class Unless 
    // That Is The Behavior You Are Looking For - Keep In Mind
    // Every Inherited Class & Outside Class Can Change Them.
protected:
    // Member Variables Here To Be Shared With Each Derived Class
private:
    // Member Variables Here To Be Used By Base Class Only

// ----- Member Function Section -----
public:
    virtual ~BaseProtected(); // Virtual Destructor 
    void somefunc() const; // Common Function Between All Derived Class
    virtual void allDerivedMustImplement() const; = 0 // Purely Virtual  

protected:
    // Default Constructor - Can Not Declare An Instance Of Base Class
    BaseProtected(); // Abstract Class         

   // Protected Functions Shared Between Classes
   // Protected Functions That Are Purely Virtual If Needed

private:
    // Private Functions Used By Base Class Only

}; // BaseProtected

Производный класс с возможным наследованием

class DerivedWithPossibleInheritance : public BaseProtected {
// ----- Member Variable Section -----
public:
    // Public Member If Giving Free Access   
protected:
    // Protected Members If Being Inherited From
private:
    // Private Members Unique To This Derived Class

// ----- Member Function Section ----- 
public:
    DerivedWithPossibleInheritance(); // Default Constructor
    virtual ~DerivedWithPossibleInheritance(); // Virtual Destructor

    void uniqueFunctionForThisClass() const;
    void allDerivedMustImplement() const override;

private:
    // Private Functions Unique To This Class

}; // DerivedWithPossibleInheritance 

Производный класс, который нельзя унаследовать от

class DerivedClassCanNotBeInheritedFrom sealed : public BaseProtected {
// ----- Member Variable Section -----
public: 
    // Public Members Variables
protected:
    // Should Not Have Member Variables Here For This Class Can Not Be Inherited from
private:
    // Private Members Variables

// ----- Member Function Section ------
public:
    DerivedClassCanNotBeInheritedFrom(); // Default Constructor
    virtual ~DerivedClassCanNotBeInheritedFrom(); // Default Virtual Destructor

    void anotherUniqueFunctionForThisClass() const;
    void allDerivedMustImplement() const override;

protected:
    // There Should Not Be Any Functions Here This Can Not Be Inherited From

private:    
    // Private Member Functions Here

}; // DerivedClassCanNotBeInheritedFrom

Я продемонстрировал здесь ключевое слово, запечатанное при работе с наследованием и полиморфизмом. Если вы не хотите, чтобы ваш класс был получен из последующего использования, используйте запечатанное ключевое слово.

Что касается объявления любого базового класса, отдельного класса или одного синглетного класса с частным конструктором, то необходимо использовать ключевое слово friend. Существует множество типов реализаций, которые могут быть задействованы здесь, чтобы показать их, но концепция предотвращения унаследования класса одинакова. Лично я не использовал ключевое слово final, но я использовал ключевое слово запечатанное, и оно работает очень хорошо.

Я не использовал наследование классов, кроме публичного: поэтому, чтобы ответить на ваши вопросы с точки зрения защищенного или частного наследования, я не очень хорошо знаком. Но, возможно, использование ключевого слова sealed может помочь вам.