Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 [ 38 ] 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

Derived::-Derivedо throw(B2,м2); Ошибка

В чем здесь проблема? Две функции созданы неверно, поскольку когда вы перекрываете любую унаследованную виртуальную функцию, спецификация исключений вашей производной функции должна представлять собой ограниченную версию спецификации исключений в базовом классе. В конце концов, только это и имеет смысл: если бы это было не так, то это означало бы, что код, который вызывает функцию посредством указателя на базовый класс, может получить исключение, которое, как обсшает базовый класс, не может быть сгенерировано. Например, считая допустимым контекст примера 19-1(6), рассмотрим следующий код:

Base* р = new Derived;

здесь может быть сгенерировано исключение в2 или м2, несмотря на то, что Base::-Base обещает не генерировать никаких исключений, кроме в2: delete р;

Это еше одна существенная причина для того, чтобы деструкторы имели спецификации исключений throwO или не имели их вообще. Кроме того, деструкторы никогда не должны генерировать исключений и всегда должны разрабатываться так, как если бы они имели спецификацию исключений throwO, даже если она не указана явно (см. [SutterOO], где есть подраздел "Деструкторы, генерирующие исключения, и почему они неприемлемы").

> Рекомендация

Никогда не позволяйте деструкторам генерировать исключения. Всегда разрабатывайте деструкторы так, как если бы они имели пустые спецификации исключений.

Никогда не используйте спецификации исключений, кроме, возможно, пустых, но я советую избегать и их (см. задачу 13).

Это еще одна причина быть осторожными с виртуальными операторами присваивания. См. раздел 29 в [Meyers96] и раздел 76 в [Dewhurst03], где подробнее рассказано об опасностях виртуального присваивания и о том, как их избежать.

> Рекомендация

Не делайте операторы присваивания виртуальными.

Теперь рассмотрим последовательно все четыре неявно генерируемые функции.

Неявный конструктор по умолчанию а) конструктор по умолчанию

Конструктор по умолчанию неявно объявляется в случае, когда вы не объявили ни одного собственного конструктора. Такой неявно объявленный конструктор является открытым и встраиваемым.

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

Такой пустой конструктор по умолчанию некорректен, если бы был некорректен такой конструктор по умолчанию, который бы вы написали самостоятельно



(например, если какой-либо из базовых классов или членов ие имеет конструктора по умолчанию).

Неявный копирующий конструктор

б) копирующий конструктор

Копирующий конструктор неявно объявляется в том случае, если вы не объявили его самостоятельно. Неявно объявленный копирующий конструктор открытый и встраиваемый, он принимает в качестве параметра ссылку, по возможности на константный объект (это возможно тогда и только тогда, когда все базовые классы и члены имеют копирующие конструкторы, принимающие в качестве параметров ссылку на const или const volatile), и на неконстантный, если это невозможно.

Стандарт в большинстве случаев игнорирует ключевое слово volatile. Компилятор изо всех сил будет стараться добавить квалификатор const к параметру неявно объявленного копирующего конструктора (и копирующего оператора присваивания), когда это возможно, чего нельзя сказать о volatile.

Неявно объявленный копирующий конструктор становится неявно определенным при попытке его реального вызова для копирования объекта данного типа, выполняет почленное копирование базовых подобъектов и членов и может генерировать любое из исключений, которые могут генерировать копирующие конструкторы базовых классов или членов. Такой копирующий конструктор некорректен, если недоступен хоть один из копирующих конструкторов базовых классов или членов (или возникает неоднозначность при выборе копирующего конструктора).

Неявный копирующий оператор присваивания

в) копирующий оператор присваивания

Копирующий оператор присваивания неявно объявляется, если вы не объявили его самостоятельно. Неявно объявленный копирующий оператор присваивания является открытым и встраиваемым и возвращает ссылку на неконстантный объект, которому выполнено присваивание. Оператор по возможности получает ссылку на константный объект (это возможно тогда и только тогда, когда все базовые классы и члены имеют операторы копирующего присваивания, которые получают в качестве параметра константную ссылку), или ссылку на неконстантный объект в противном случае. Как и в случае копирующего конструктора, квалификатор volatile не используется.

Неявно объявленный оператор копирующего присваивания неявно определен только в случае реальной попытки присваивания объекта данного типа, и выполняет почленное присваивание подобъектов базового класса и членов (включая возможные множественные присваивания подобъектов виртуальных базовых классов), и может генерировать исключения всех типов, которые могут генерировать копирующие присваивания базовых классов и членов. Копирующее присваивание некорректно, если любой из базовых классов или членов является константным, ссылкой, либо имеет недоступный или неоднозначный оператор копирующего присваивания".

Неявный деструктор

г) деструктор

Деструктор неявно объявляется в случае, если вы не объявили его самостоятельно. Неявно объявленный деструктор открытый и встраиваемый.

Неявно объявленный деструктор неявно определяется только в том случае, если вы действительно пытаетесь вызвать сго, причем он имеет тот же вид, как если бы вы

Оператор копирующего присваивания может копировать массивь:, являющиеся членами классов, что дает единственный способ неявно [поэлементно] копировать массив. - Прим. ред.



написали пустой деструктор самостоятельно, причем такой деструктор может генерировать все исключения, которые могут генерировать деструкторы базовых классов и членов. Неявный деструктор недействителен, если хотя бы один из базовых классов или членов имеет недоступный деструктор, или один из деструкторов базовых классов виртуален и не все деструкторы базовых классов или членов имеют идентичные спецификации исключений (см. раздел "Спецификации исключений неявно определенных функций", стр. 127).

Член auto ptr

2. Какие функции неявно объявлены и созданы компилятором в следующем классе х? С какими сигнатурами?

пример 19-2 class X {

auto ptr<int> i ;

Небольшое отступление. Этот пример просто иллюстрирует правила, которым подчиняется неявное объявление и определение функций, так что не стоит считать его образцом хорошего стиля. Вообще говоря, применения auto ptr в качестве членов класса (да и в других ситуациях) следует избегать; предпочтительно использовать класс shared ptr, которого еще нет в стандартной библиотеке, но он уже включен в п р е двар ител ьн у ю редакцию следующей версии стандарта С+ + .

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

inline х::х() throwO : i 0 { }

inline х::х( х& other ) throwO : i ( other.i ) { }

inline X& X::operator=( X& other ) throwO

{i =other.i ; return*thi s;} inline x: :~xO throwO { }

Копирующий конструктор и оператор копирующего присваивания получают в качестве параметров ссылки на неконстантные объекты. Это обусловлено тем, что так же поступают копирующий конструктор и оператор копирующего присваивания auto ptr. Аналогично, все эти функции имеют пустые спецификации исключений, так как в состоянии их обеспечить - ни одна аналогичная операция auto ptr не генерирует исключений (как, впрочем, вообще все операции auto ptr).

Заметим, что копирующий конструктор и оператор копирующего присваивания класса auto ptr передают владение. Это может оказаться не тем действием, на которое рассчитывает автор х, так что класс х, скорее всего, должен обеспечивать собственные версии копирующего конструктора и оператора копирующего присваивания (Дополнительную информацию по этому вопросу можно найти в [Sutter02].)

Семейные проблемы

3. Пусть у вас есть базовый класс, который требует, чтобы все производные классы не ис-нользовали ни одной неявно объявленной и созданной компилятором функции. Например:

пример 19-3 class Count { public:

этим комментарием автор класса Count документирует,

что производные классы должны наследоваться

виртуально, и что их конструкторы должны вызывать

конструктор класса Count специального назначения.

count( /* Специальные параметры */ );



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 [ 38 ] 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85