Как запретить наследование от класса c
Тут кто-то спрашивал про то, как запретить наследование — у Страуструпа в FAQ есть ответ с комментариями. Здесь привожу свой перевод (простите за качество, переводил навскидку)
Могу ли я запретить наследоваться от моего класса?
Да, но зачем? На это может быть вообще говоря две причины:
для эффективности: чтобы не пользоваться виртуальными вызовами
для безопасности : чтобы быть уверенным в том, что класс не используется как базовый (к примеру, быть уверенным, что объекты можно безбоязненно копировать [slizing — не знаю что это])
Из моего опыта, боязнь потери эффективности обычно неуместна. В C++ вызовы виртуальных функций быстры настолько, что их использование в классах, спроектированных с виртуальными функциями не даёт заметных издержек в сравнении с альтернативными решениями, использующими обычные функции. Также заметьте, что использование механизма вирутальных вызовов обычно происходит только при вызовах через указатель или ссылку. При вызове функций напрямую для именованного объекта виртуальный вызов может быть довольно просто оптимизирован и не давать вообще никаких издержек.
С другой стороны, когда есть логичная причина на это, запретить дальнейшее наследование можно. К сожалению, решение не самое красивое. Оно опирается на то, что самый последний производный класс [most derived class] в иерархии обязан сконструировать виртуальную базу.
class Usable; class Usable_lock < friend class Usable; private: 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
Конец перевода, далее мои комментарии
На самом деле, «некрасивость» решения сводится к тому, что эта техника не может быть легко расширена шаблонами:
templatetypename T> class nonderivable < friend class T; private: nonderivable() <> >; class my_class : public virtual nonderivable < public: my_class(); >;
Это работать не будет, потому что [7.1.5.3/2]
If the identifier resolves to a typedef-name or a template typeparameter, the elaborated-type-specifier is illformed. [Note: this implies that, within a class template with a template type-parameter T, the declaration friend class T; is illformed. ]
Поэтому для каждого класса, который хочет воспользоваться подобной техникой и стать финальным в иерархии придётся писать «friend class concrete-class-name-to-be-final».
Запрет наследования. Ключевое слово sealed . Особенности применения. Пример
В языке C# введено ключевое слово sealed , которое используется в двух случаях:
- когда нужно запретить наследование от некоторого класса. В многих иерархиях, классы, которые размещены на нижних уровнях могут быть обозначены как sealed ;
- когда нужно запретить переопределение некоторого метода в иерархии классов. Эта ситуация возможна, если в иерархии классов методы унаследованных классов переопределяют виртуальные методы базовых классов.
2. Применение ключевого слова sealed для класса. Общая форма. Пример
Общая форма использования ключевого слова sealed для класса, следующая:
sealed class ClassName < // . >
- ClassName – имя класса, который нужно запретить наследовать.
Если из класса ClassName попробовать унаследовать другой класс, то возникнет ошибка на этапе компиляции. При этом, сам класс ClassName может быть унаследован от другого класса.
Например, в следующем коде
// Класс, который запрещено наследовать sealed class A < // . > // Класс, который пробует унаследовать класс A class B : A < // . >
компилятор выдаст ошибку
'B': cannot derive from sealed type 'A'
3. Применение ключевого слова sealed для метода. Общая форма. Пример
Ключевое слово sealed может быть применено для метода также. Использование sealed для метода означает, что метод запрещено переопределять в унаследованных классах. Значит, такой метод должен быть объявлен с ключевым словом override . А это, в свою очередь, означает, что класс с sealed-методом должен наследовать другой базовый класс, который содержит метод с таким же именем.
Чтобы к методу класса применить ключевое слово sealed , нужно чтобы выполнялись следующие условия:
- класс должен быть унаследован от другого (базового) класса;
- в базовом классе должен быть объявлен метод, который переопределяется в данном (производном) классе. Этот метод базового класса также должен быть объявлен с ключевыми словами virtual или override (если базовый класс также унаследован).
Общая форма использования ключевого слова sealed для метода следующая:
class ClassName : BaseClassName < // . access_modifier sealed override MethodName(parameters) < // . > >
- ClassName – имя класса, в котором объявлен sealed-метод с именем MethodName ;
- BaseClassName – имя класса, который есть базовым для класса ClassName . Запрет переопределения метода в классе предусматривает, что этот класс унаследован от другого базового класса, в котором объявлен одноименный переопределяемый метод. Поэтому, если для некоторого метода в классе нужно применить ключевое слово sealed , этот класс обязательно должен быть унаследован от другого класса;
- access_modifier – один из модификаторов доступа public , protected , internal , protected internal . Модификатор доступа private не может быть применен к sealed-методу;
- MethodName – имя метода, который объявляется как sealed . Данный метод должен быть объявлен с ключевым словом override . Это значит, что данный метод переопределяет одноименный метод базового класса BaseClassName .
Пример.
Объявляется иерархия классов A , B , C . В классе C объявляется метод, который запрещено переопределять.
// Базовый класс для классов B, C class A < // Метод, который переопределяется в производных классах public virtual void Method() < Console.WriteLine("MethodA()"); > > // Класс, который наследует класс A class B : A < // В этом классе объявляется метод, который не запрещено // переопределять в производных классах public override void Method() < Console.WriteLine("MethodB()"); > > // class C : B < // Объявляется метод, который запрещено переопределять // в производных классах - с ключевым словом sealed public override sealed void Method() < Console.WriteLine("MethodC()"); > >
Если из класса C попробовать унаследовать класс D , и в классе D объявить метод, который переопределяет метод Method() класса C как показано далее
. // Класс D наследует класс C class D : C < // Ошибка! Метод Method() нельзя переопределять public override void Method() // ошибка! < Console.WriteLine("MethodD()"); > > .
то компилятор выдаст ошибку
D.Method(): cannot override inherited member C.Method() because it is sealed
Чтобы поправить ситуацию нужно выполнить одно из двух действий (в зависимости от поставленной задачи):
- убрать слово sealed перед именем метода в классе C ;
- поменять слово override на слово new перед именем метода в классе D .
4. Можно ли в sealed -классе объявить sealed метод?
Да, можно. В приведенном ниже примере объявляется sealed-класс и sealed-метод в этом классе.
// Базовый класс class A < // . // виртуальный метод protected virtual void Method() < >> // Класс, который наследует класс A sealed class B : A < // . // метод, который переопределяет одноименный метод базового класса protected sealed override void Method() < >>
Хотя класс B не может быть унаследован, компилятор такой код не запрещает.
Связанные темы
- Наследование. Базовые понятия. Преимущества и недостатки. Общая форма. Простейшие примеры. Модификатор доступа protected
- Использование конструкторов в классах при наследовании. Ключевое слово base при наследовании
- Доступ к элементам базового класса из унаследованного класса. Ключевые слова base , new . Примеры
- Примеры создания иерархий классов с использованием наследования
Как запретить наследование класса в C#
Для предотвращения наследования от класса необходимо объявить его с модификатором sealed. Например, если мы не хотим чтобы от класса SomeClass можно было наследоваться, то объявляем его так:
sealed class SomeClass < // Объявление класса >
Отслеживать
ответ дан 21 окт 2010 в 21:13
Nicolas Chabanovsky Nicolas Chabanovsky
51.4k 86 86 золотых знаков 267 267 серебряных знаков 505 505 бронзовых знаков
В качестве альтернативы, можно запретить и создание экземпляров этого класса с помощью ключевого слова static (под капотом, будет использована пара модификаторов: abstract sealed ).
static class Helper < // допустимы лишь статические методы >
Отслеживать
ответ дан 12 апр 2015 в 0:17
Sergey Teplyakov Sergey Teplyakov
7,215 1 1 золотой знак 29 29 серебряных знаков 35 35 бронзовых знаков
-
Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2024 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2024.1.3.2953
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Как запретить наследование от класса c

Запрет наследования
В этой статье, я хочу сказать пару слов о явном запрете наследования. Точнее о том, как можно запретить использование написанного Вами класса, в качестве базового для других. Я постараюсь доступно и коротко обосновать зачем, и вообще это нужно ли это делать. Материал этой статьи так или иначе связан с темой наследования классов, так что, если Вы еще не знакомы с механизмом наследования, то можете не читать эту статью, а перейти к изучению основ. Вот Вам ссылка на урок, посвященный наследованию классов в C#. А для тех кто уже в теме, я продолжу повествование…
И так, продолжим! Сначала я расскажу, зачем вообще запрещать использование наших классов в качестве базовых. На самом деле, всё просто, если мы изначально не разрабатываем наш класс для использования в качестве базового, то это уже повод запретить наследовать другие классы от него! Почему, да хотя бы по тому, что не подготовленный для «отцовства» класс, хорошим «отцом» скорее всего не станет!
Давайте вспомним, для чего обычно программисты используют механизм наследования ? Ну, в первую очередь, приходит в голову мысль о повторном использовании уже написанного кода. Т.е. мы имеем некий класс, но хотим расширит его функциональность. Однако, создавать еще один класс, повторно реализую функциональность первого и дополняя его новшествами мы не ходим (особенно, если не мы авторы первого класса и даже не знаем как реализована его функциональность), вместо этого мы просто «получаем по наследству» эту самую функциональность и всего лишь, дополняем её. Да, для этого можно использовать наследование, но совсем не обязательно! Можно решить эту задачу более гибким, и дешевым способом (пока поверьте на слово, потом, я напишу отдельную статью про то, как это сделать).
Ну и вторая цель использования наследования — это установка определенных отношений между базовым классом, и классом наследником. Объекты производных классов система может воспринимать и как объекты базового. Если объяснять на пальцах, то тигр является не только тигром, но еще и животным (но не каждое животное является тигром). Эти отношения между классами, выходят на сцену при использования так называемого принципа подстановки. Другими словами, при работе с объектами производных классом, через ссылку базового типа. Но эффективное применение упомянутого выше принципа будет невозможно, если автор базового класса не предусмотрел возможность использования своего детища в качестве базового для других классов. Например, не сделал какой-то метод класса виртуальным. Вот что я имел ввиду, когда говорил про неподготовленные для «отцовства» классы.
Если Вы не совсем понимаете о чем я сейчас говорил, изучите следующий урок.
Так вот, если Вы сами пишете весь код, то Вы просто можете договориться с собой, и не использовать какие-то классы в качестве базовых для других. Но в реальной практике, Вы, как разработчик потенциально базового класса можете быть даже не знакомы с программистом, решившим использовать Ваше творение в качестве базы для своего! И это может привести к неприятным сюрпризам для того человека. Но в C# есть возможность запретить использование класса в качестве родительского на этапе его разработки. И делается это очень просто, нужно добавить перед ключевым словом class ключевое слово sealed.
И всё, теперь теперь программист, не имеющий доступа к исходникам Вашего класса, не сможет использовать его как базовый для своих. А теперь покажу это на практике:
//"Отцом" уже не будет! sealed class SomeClass < //Тело класса >
В следующей статье, я, пожалуй, расскажу о том, как можно заменить наследование, если нам нужно просто повторное использование функциональности.
Добавить комментарий Отменить ответ
Для отправки комментария вам необходимо авторизоваться.