Как определить запечатанный класс в C ++? - PullRequest
52 голосов
/ 17 января 2011

Как остановить наследование класса другим классом.

Ответы [ 6 ]

84 голосов
/ 17 января 2011

C ++ 11 решение

В C ++ 11 вы можете запечатать класс, используя ключевое слово final в определении:

class A final  //note final keyword is used after the class name
{
   //...
};

class B : public A  //error - because class A is marked final (sealed).
{                   //        so A cannot be derived from.
   //...
};

Чтобы узнать другие варианты использования финала, смотрите мой ответ здесь:


C ++ 03 решение

Код Бьярна Страуструпа : Могу ли я остановить людей, выходящих из моего класса?

class Usable;
class Usable_lock {
    friend class Usable;
private:
    Usable_lock() {}
    Usable_lock(const Usable_lock&) {}
};

class Usable : public virtual Usable_lock {
public:
    Usable();
    Usable(char*);
};
Usable a;

class DD : public Usable { };

DD dd;  // error: DD::DD() cannot access
        // Usable_lock::Usable_lock(): private  member

Generic_lock

Таким образом, мы можем использовать шаблон, чтобы сделать Usable_lock достаточно универсальным, чтобы запечатать любой класс:

template<class T>
class  Generic_lock 
{
    friend T;
    Generic_lock() {}                     //private
    Generic_lock(const Generic_lock&) {}  //private
};

class Usable : public virtual Generic_lock<Usable>
{
public:
    Usable() {}
};

Usable a; //Okay
class DD : public Usable { };

DD dd; //Not okay!
10 голосов
/ 17 января 2011

Есть два способа, простой дешевый и правильный.Два ответа @Naveen и @Nawaz связаны с правильным ответом, который требует ручного создания класса sealer для каждого класса, который вы действительно хотите запечатать.

Не дуракспособ, который используется в библиотеках Adobe использует шаблонный класс для этого.Проблема в том, что вы не можете объявить аргумент шаблона как друга, и это означает, что вам придется переключиться с private на менее безопасный protected:

template <typename T>
class sealer {
protected: sealer() {}
};
class sealed : virtual sealer<sealed> {};

И вы можете автоматизировать его с помощьюмакрос (я не помню точный вид макроса в коде Adobe):

#define seal( x ) virtual sealer<x>
class sealed : seal(sealed) 
{};

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

class derived : sealed {};
int main() {
   derived d;  // sealer<T>::sealer() is protected within this context
}

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

class derived : sealed, sealer<sealed> {};
int main() {
   derived d;
};

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

template <typename T>
class sealer {
   sealer() {}
   friend class T; // Incorrect in C++03
};
8 голосов
/ 11 августа 2011

C ++ 11 добавляет возможность предотвращать наследование от классов или просто предотвращать переопределение методов в производных классах. Это делается с помощью специального идентификатора final. Например:

class Base final { };

class Derived1 : Base { }; // ill-formed because the class Base has been marked final

или

class Base {
    virtual void f() final;
};

class Derived : Base {
    void f(); // ill-formed because the virtual function Base::f has been marked final

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

0 голосов
/ 29 декабря 2014

Основано на часто задаваемых вопросах Бьярна Страуструпа http://www.stroustrup.com/bs_faq2.html#no-derivation с небольшой модификацией без использования ключевого слова друга:

// SEALED CLASS DEFINITIONS
class Usable_lock {
protected:
    Usable_lock() {}
    Usable_lock(const Usable_lock&) {}
};
#define sealed_class private virtual Usable_lock

// SEALED CLASS USAGE EXMAPLES
class UsableLast : sealed_class {
public:
    UsableLast(){}
    UsableLast(char*){}
};
class DD : public UsableLast {};

// TEST CODE
template <class T> T createInstance() {
    return T();
}
int main()
{
    createInstance<UsableLast>();
//  createInstance<DD>();
    return 0;
}
0 голосов
/ 01 апреля 2014

Следующий код показывает, как определить запечатанный класс в C ++ / CLI.

class A sealed
{
    //here goes the class code
};

class B : public A
{
};

Теперь B: не может наследовать от A, так как он был объявлен «запечатанным». Также подробное описание запечатанного ключевого слова можно найти здесь http://msdn.microsoft.com/en-us/library/0w2w91tf.aspx

Обновление: добавлен C ++ / CLI, также в других ответах был продемонстрирован новейший C ++ 11 способ достижения того же, используя ключевое слово final.

0 голосов
/ 17 января 2011

Вы не можете. C ++ не является Java или C #. И тоже нет смысла, никогда, ИМХО.

...