Программирование >>  Обобщенные обратные вызовы 

1 ... 34 35 36 [ 37 ] 38 39 40 ... 84


Поскольку явно объявленных конструкторов нет, семантика неявно сгенерированного конструктора по умолчанию заключается в вызове конструкторов по умолчанию базовых классов и членов. Следовательно, спецификация исключений неявно сгенерированного конструктора класса с должна позволять генерацию любых исключений, которые могут быть сгенерированы конструкторами по умолчанию базовых классов или членов. Если хоть один базовый класс С или его член имеет конструктор по умолчанию без явной спецификации исключений, то неявно объявленный конструктор по умолчанию С может генерировать любое исключение:

public:

inline С::С(); может генерировать что угодно

Если все базовые классы и члены С имеют конструкторы по умолчанию с явно указанными спецификациями исключений, то неявно объявленный конструктор С может генерировать исключения любого из типов, упомянутых в этих спецификациях исключений:

public:

inline с::С() throw (

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

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

пример 19-1(6): опасный момент!

class Derived; class Base { public:

несколько искусственный пример, но технически вполне

возможно использовать этот метод для объявления

оператора присваивания Base, который получает в

качестве аргумента Derived. Перед тем как даже просто

подумать применить такой метод, обязательно прочтите

подраздел 33 в [Meyers96].

vi rtual Bases /* или Derived* */ operator=( const Derived* ) throw( Bl ); vi rtual ~Base() throw( В2 );

class Member { publiс:

Members operator=( const Member& ) throw( Ml ) ; -MemberO throw( М2 );

class Derived : public Base {

Member m ;

неявно объявлены четыре функции: Derived::Derived(); ok

Derived::Oerived( const Derived& ); ok Derived& Derived::operator =

(const Derived*) throw(Bl,Ml); Ошибка



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

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

Base* р = new Derived;

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

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

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

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

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

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

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

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

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

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

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

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

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



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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



1 ... 34 35 36 [ 37 ] 38 39 40 ... 84

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика