Остановка наследования от базового класса после n-го наследования в C ++

У меня проблема, когда мне нужно только один раз наследовать базовый класс. После этого я хочу запретить кому-либо наследовать этот класс. Может кто-нибудь объяснить мне, как этого можно достичь. Более того, если базовый класс наследуется n раз (например, 1.10), каков будет общий способ сделать это.

Я знаю, что использование класса друзей в базовом классе может в некоторой степени решить мою проблему. Но я считаю это неуклюжим. Также я читал о Ограничьте наследование желаемым количеством классов во время компиляции. Я не смог полностью понять ответ.

Может ли кто-нибудь предложить мне лучшие способы ограничить наследование во время компиляции на С ++.


person user2033594    schedule 07.01.2019    source источник
comment
Я считаю, что глубина дерева наследования является основным препятствием для понимания класса. Молодец, что спросили, как это ограничить.   -  person Mark Ransom    schedule 07.01.2019
comment
@MarkRansom Насколько я могу судить, OP спрашивает, как ограничить ширину дерева наследования, а не его глубину.   -  person Miles Budnek    schedule 07.01.2019
comment
Да, я хочу ограничить ширину дерева наследования.   -  person user2033594    schedule 07.01.2019
comment
Вам действительно нужно разрешить определенное количество неизвестных производных классов? Или вам нужно предотвратить любые дополнительные производные классы, которых нет в конкретной предполагаемой реализации? Можете немного рассказать почему? Это звучит как проблема XY; могут быть более эффективные способы защиты от любого вреда, который могут нанести дополнительные производные классы.   -  person aschepler    schedule 07.01.2019
comment
в настоящее время базовый класс наследуется внутренней командой один раз. Теперь этот класс доступен третьим лицам. Однако я не хочу, чтобы они унаследовали этот базовый класс. Итак, это постановка проблемы   -  person user2033594    schedule 07.01.2019
comment
Вы не возражаете против того, чтобы абоненты имели доступ к его чисто виртуальному интерфейсу? Вы хотите ограничить только функциональность / реализацию?   -  person Galik    schedule 07.01.2019
comment
# Галик Нет, я не буду возражать против того, чтобы звонящие имели доступ к чисто виртуальному интерфейсу   -  person user2033594    schedule 07.01.2019
comment
Более того, было бы хорошо, если бы он мог ограничивать наследование во время выполнения. Что вы имеете в виду? мы не можем создать класс / иерархию во время выполнения ...   -  person Jarod42    schedule 07.01.2019
comment
Это звучит как неправильная функция. Задокументируйте, что делает ваш класс и как он предназначен для использования. Если кто-то злоупотребляет этим, это их проблема, а не ваша.   -  person Pete Becker    schedule 07.01.2019
comment
Пожалуйста, предоставьте четкое MCVE (и очень краткий пример того, что вы делаете в настоящее время и что хотите предотвратить). В настоящее время ваши вопросы довольно запутанные и неясные, поэтому могут быть закрыты.   -  person Walter    schedule 08.01.2019
comment
@ Jarod42 это было по ссылке, которой я поделился. В любом случае я удалил линию.   -  person user2033594    schedule 08.01.2019


Ответы (3)


в настоящее время базовый класс наследуется внутренней командой один раз. Теперь этот класс доступен третьим лицам. Однако я не хочу, чтобы они унаследовали этот базовый класс. Итак, это постановка проблемы

Проблема в том, что C ++ не предоставляет никаких средств для прямого ограничения наследования таким образом.

Указатель на идиому реализации может помочь вам в любом случае:

class PublicToUser final
{
    class PrivateToUser;
    std::unique_ptr<PrivateToUser> implementation;
public:
    // ***
};

Теперь это будет частный внутренний класс, который фактически наследует, но его можно полностью скрыть, т.е. е. вам вообще не нужно открывать этот базовый класс пользователю, то, что не видно, не будет унаследовано ...

person Aconcagua    schedule 07.01.2019

У меня проблема, когда мне нужно только один раз наследовать базовый класс. После этого я хочу запретить кому-либо наследовать этот класс.

Начиная с C ++ 11, вы можете просто использовать final для предотвращения дальнейшего разделения класса на подклассы:

class BaseClass { };

class DoNotInheritClass final : public BaseClass { };

Для ограничения количества подклассов из одного базового класса, по-видимому, нет встроенной функции C ++. Вам нужно самостоятельно отслеживать подклассы, добавляя статические переменные. Вот существующий ответ: Подсчет экземпляров отдельных производных классов

person artm    schedule 07.01.2019
comment
Мне нужно один раз унаследовать от базового класса. Следовательно, это не поможет. Это не позволило бы никому наследовать от базового класса хотя бы один раз. - person user2033594; 07.01.2019
comment
Значит, вы хотите унаследовать от BaseClass только один раз и не дальше, не так ли? - person artm; 07.01.2019
comment
Нет .. Более того, я хочу, чтобы он был универсальным, как упоминалось в вопросе. Допустим, я хочу, чтобы базовый класс наследовался дважды или трижды, тогда каков будет подход - person user2033594; 07.01.2019
comment
см. этот ответ, затем stackoverflow.com/questions/ 772655 / - person artm; 07.01.2019

Вот несколько идей, которые могут помочь, если вы не можете использовать решение PIMPL из другого ответа. Они основаны либо на CRTP, либо на тегах конструктора:

#include <type_traits>

struct A;

template<typename T>
struct Base final {

};

// Specialise Base for A to be non-final
template<>
struct Base<A> {};

struct A : Base<A> {};

Идея первого фрагмента состоит в том, чтобы иметь final CRTP-базу и добавить неокончательную специализацию для класса A. Очевидно, что это позволяет пользователям (или вам) при необходимости определять больше разрешенных производных, но предотвращает случайное наследование.

template<char...>
struct password {};

struct Base2 {

    Base2() = delete;

    template<char... pwd>
    Base2(password<pwd...> ) {
        using expected_pwd = password<'t','o','p','s','e','c','r','e','t'>;
        static_assert(std::is_same_v<password<pwd...>, expected_pwd>);
    }
};

struct A2 : Base2 {

    A2() : Base2(password<'t','o','p','s','e','c','r','e','t'>{}) {}
};

struct A3 : Base2 {

    // No way to construct Base2 without correct password :)
    A3() : Base2(password<'b', 'a', 'm', 'm'>{}) {}
};

Идея этого второго фрагмента (который является скорее шуткой, чем реальным кодом ...) состоит в том, чтобы заменить конструктор по умолчанию класса Base2 структурой password. Внутри реализации вы проверяете пароль, присвоенный конструктору. В реализации производного класса вы просто вызываете конструктор Base2 с правильным паролем. Конечно, ваши пользователи могут быть производными от базового класса, но поскольку пароль полностью скрыт в реализации, нет возможности создать производные объекты :)

int main(){

    auto a = A{};
    auto a2 = A2{};
    auto a3 = A3{};  // fails
    return 0;
}
person florestan    schedule 07.01.2019