Есть ли способ запретить подклассы моего класса? - PullRequest
16 голосов
/ 08 августа 2009

Скажем, у меня есть класс "Base" и класс "Derived", который является подклассом Base и обращается к защищенным методам и членам Base.

Что я хочу сейчас сделать, так это сделать так, чтобы никакие другие классы не могли создавать подкласс Derived. В Java я могу сделать это, объявив производный класс "final". Есть ли какой-нибудь трюк C ++, который может дать мне тот же эффект?

(В идеале я хотел бы сделать так, чтобы никакой другой класс, кроме Derived, не мог также создавать подклассы Base. Я не могу просто поместить весь код в один класс или использовать ключевое слово friend, так как Base и Derived оба шаблонный, с Base, имеющим меньше аргументов шаблона, чем Derived ....)

Ответы [ 5 ]

13 голосов
/ 07 сентября 2012

Начиная с C ++ 11, вы можете добавить ключевое слово final (технически специальный идентификатор, поскольку на самом деле это не ключевое слово) к вашему классу, например

class Derived final
{
...

Подробнее о последнем ключевом слове читайте на http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final

9 голосов
/ 08 августа 2009

Вы можете иметь приватный конструктор для 'Derived' и общедоступную статическую функцию Create для реализации

5 голосов
/ 08 августа 2009

Самый простой способ запретить создание подклассов - сделать конструктор частным:

class Foo
{
private:
    Foo() {}

public:
    static Foo* CreateFoo() { return new Foo; }
};

Редактировать: Спасибо Indeera за указание на то, что для этого нужен статический метод Factory

3 голосов
/ 08 августа 2009

Нет простого и понятного способа сделать это.

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

В конечном счете, вам нужно , чтобы сделать подклассы невозможными ? Разве этого недостаточно, чтобы указать, что «выход из этого класса - плохая идея»?

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

Защитите свой код от Мерфи, а не от Макиавелли. ;)

1 голос
/ 08 августа 2009

Поскольку вы используете шаблоны, я подумал, что последняя часть вашего вопроса о запрете любого класса, кроме Derived для подкласса из Base, может быть выполнена с использованием соответствующих частичных специализаций.

Следующий фрагмент кода - это то, что я придумал, но необходимая сложность лишь усиливает ответ jalf. Стоит ли оно того? Если что-то и помогло мне понять частичную специализацию больше, чем выработать технику, которую я бы использовал на практике.

Я использую COMMON, чтобы указать параметр общего шаблона между Base и Derived и EXTRA, чтобы обозначить дополнительные параметры, которые, как вы говорите, имеет Derived. Фактические цифры могут быть такими, как я только что выбрал для них один и два соответственно.

// Forward declaration of class Derived
template< class COMMON
        , class EXTRA1
        , class EXTRA2 >
class Derived;


// Definition of general class template Base
template< class SUBCLASS
        , class COMMON >
class Base
{
private:
    Base() {}
};


// Definition of partial specialisation of template class Base to open up
// access to the constructor through friend declaration.
template< class COMMON
        , class EXTRA1
        , class EXTRA2 >
class Base< Derived< COMMON, EXTRA1, EXTRA2 >
          , COMMON >
{
private:
    Base() {}

    friend class Derived< COMMON, EXTRA1, EXTRA2 >;
};


// Definition of class Derived
template < class COMMON
         , class EXTRA1
         , class EXTRA2 >
class Derived
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 >
                 , COMMON >
{
public:
    static Derived* create() { return new Derived; }

private:
    Derived() : Base< Derived< COMMON, EXTRA1, EXTRA2 >
                    , COMMON >()
    {
    }
};


// Definition of class HonestDerived.
// It supplies itself as the SUBCLASS parameter to Base.
template < class COMMON
         , class EXTRA1
         , class EXTRA2 >
class HonestDerived
    : public Base< HonestDerived< COMMON, EXTRA1, EXTRA2 >
                 , COMMON >
{
public:
    HonestDerived() : Base< HonestDerived< COMMON, EXTRA1, EXTRA2 >
                          , COMMON >()
    {
    }
};


// Definition of class DishonestDerived
// It supplies Derived rather than itself as the SUBCLASS parameter to Base.
template < class COMMON
         , class EXTRA1
         , class EXTRA2 >
class DishonestDerived
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 >
                 , COMMON >
{
public:
    DishonestDerived() : Base< Derived< COMMON, EXTRA1, EXTRA2 >
                             , COMMON >()
    {
    }
};


template< class COMMON, class EXTRA1, class EXTRA2 >
class DerivedFromDerived
    : public Derived< COMMON, EXTRA1, EXTRA2 >
{
public:
    DerivedFromDerived() : Derived< COMMON, EXTRA1, EXTRA2 >()
    {
    }
};

// Test partial specialisation gives Derived access to the Base constructor
Derived< int, float, double >* derived
    = Derived< int, float, double >::create();

// Test that there is no access to the Base constructor for an honest subclass
// i.e. this gives a compiler error
HonestDerived< int, float, double > honestDerived;

// Test that there is no access to the Base constructor for a dishonest subclass
// i.e. this gives a compiler error
DishonestDerived< int, float, double > dishonestDerived;

// Test that there is no access to the Derived constructor
// i.e. this gives a compiler error
DerivedFromDerived< int, float, double > derivedFromDerived;

Этот код был протестирован с gcc 4.3.2.

Обратите внимание, что альтернативой объявлению друга может быть защита конструктора при частичной специализации Base, но тогда это позволит классам, таким как DishonestDerived, работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...