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

1 ... 36 37 38 [ 39 ] 40 41 42 ... 84


который должен всегда быть доступен для производных классов и который обычно должен быть открытым и виртуальным, как в примере 19-4, или защищенным и невиртуальным (см. задачу 18).

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

Что это нам дает?

class Derived : private Base { int i ;

конструктор no умолчанию: объявлен, но определение некорректно (нет конструктора Base по умолчанию)

копирующий конструктор: объявлен, но определение

некорректно (копирующий конструктор Base недоступен)

копирующее присваивание: объявлено, но определение некорректно (копирующее присваивание Base недоступно)

деструктор: все в порядке, будет компилироваться

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

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

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

Альтернатива №1: сделать функции базового класса неоднозначными. Этот метод ничуть не лучше: он так же не влияет на неявно сгенерированный деструктор и требует большего количества работы.

Альтернатива №2: предоставить базовые версии функций, аварийно завершающие работу программы. Например, мы можем заставить их генерировать исключение std: :logic error. Это также не приводит к решению вопроса о неявно генерируемом деструкторе (не нарушает работу всех возможных деструкторов), а также превращает ошибку времени компиляции в ошибку времени выполнения, что существенно хуже.

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

Предпочитайте ошибки времени компиляции ошибкам времени выполнения.

Альтернатива №3: обеспечить чисто виртуальные базовые версии. Это бесполезно: метод НС применим к конструкторам (как к конструктору по умолчанию, так и к копирующему конструктору); он не в состоянии помочь нам в случае копирующего присваивания, поскольку производные версии имеют отличающиеся сигнатуры; и он не в состоянии справиться с деструкторами, так как неявно генерируемая версия будет удовлетворять требованию определения деструктора.

Альтернатива №4: использование виртуального базового класса без конструктора по умолчанию. Этот метод заставляет каждый производный класс явным образом вызывать



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

Резюме

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



Управление памятью и ресурсами

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

Насколько хорошо вы понимаете, как используется память различными стандартными контейнерами? Можете ли вы с уверенностью утверждать, что контейнер list, содержащий 1 ООО объектов, будет требовать меньший объем памяти, чем, например, контейнер set с 1 ООО объектов того же типа? Или, возвращаясь к вопросам безопасности исключений: поможет ли использование версии оператора new, не генерирующей исключений, сделать код более безопасным? И наконец, почему на многих современных платформах не имеет смысла беспокоиться о возможных отказах при иы-полнении оператора new?



1 ... 36 37 38 [ 39 ] 40 41 42 ... 84

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