|
Программирование >> Полиморфизм без виртуальных функций в с++
компилятор не должен запрещать программисту явно обходить систему типов. Так, в [Stroustrup, 1992b] читаем: Иногда бывает полезно, чтобы объекты предстовлялись пользователям константными, но но сомом деле меняли свое состояние. Токие клоссы можно создавать с помощью явного приведения типов: class XX { int а,- int calls of f; int f{) const { {{XX*)this)->calls of f++; return a; } . . . Если есть явное преобразование типа, значит, программа спроектировоно недостаточно удачно. Изменение состояния константного объекта может вводить в заблуждение, в определенном контексте приводить к ошибком. Если же объект находится в постоянной памяти, то этот прием и вовсе не работает. Лучше вынести переменную часть объекта в отдельный объект: class XXX { int а; int& calls of f; int f{) const { calls of f++; return a; } . . . XXX() : calls of f{*new int) {/*...*/} ~XXX() { delete &calls of f; /* ... */ } . . . He стоит забывать, что основная цель модификатора const - специфицировоть интерфейс, о не помочь оптимизатору. Следует токже отметить, что свобода действий и гибкость иногда бывают полезны, но могут применяться и неправильно . Включение оператора const cast (см. раздел 14.3.4) позволяет программисту отличить приведения типов с целью отбрасывания const от тех, которые применяются для других манипуляций с типами. 13.3.2. Уточнение определения const Чтобы гарантировать, что некоторые, но не все константные объекты можно разместить в постоянной памяти (ПЗУ), я принял следующее правило: любой объект, имеющий конструктор (то есть нуждающийся в инициализации во время выполнения), не может размещаться в ПЗУ, а все остальные константные - могут. Это напрямую связано с вопросом о том, что, когда и как может быть инициализировано (см. раздел 3.11.4). Данное правило позволяет инициализировать константные объекты во вре.мя выполнения, но допускает также и размещение в ПЗУ тех объектов, которые в такой инициализации не нуждаются. Типичным примером служит большой массив простых объектов, скажем, таблица, сгенерированная синтаксическим анализатором YACC. Привязка концепции const к конструктора.м была компромиссом между са.мой идеей const, соображением о том, что программисту нужно доверять, и существующей ситуацией в области аппаратного обеспечения. По предложению Джерри Шварца это правило было заменено другим, более точно отражающим мое первоначальное намерение относительно const. Объект, объявленный как const, считается неизменным с момента завершения конструирования до начала работы деструктора. Результат oiiepainin записи в объект между эти.ми дву.мя мо.ментами не определен. Когда я первоначально проектировал концепцию const, то, помнится, говорил, что в гщеале константный объект был бы записывае.мым, пока не отработа..! до конца конструктор, затем становился доступным только для чтения, а после входа в деструктор он снова мог изменяться. Можно вообразить себе тэгироваи-ную архитектуру, благодаря которой обеспечивается подобное поведение. При такой реализации возникала бы ошибка во время выполнения при попытке изменить объект, объявле1Н1ЫЙ как const. С другой стороны, .можно было бы попробовать из.менить объект, не объявленный с модификатором const, но переданный по константной ссылке или указателю. В обоих случаях пользователь сначала должен снять константность . В результате от.мена const для объекта, который первоначально был объявлен константным, и последующее его и.з.мене11ие приводят к непредсказуе.мо.му результату. Но те же действия, примененные к объекту, который не был изначально объявлен константным, вполне законны и четко определены. Отмети.м, что после такого уточнения правил семантика const не зависит от того, имеет ли тип конструктор или нет; в принципе конструктор есть у всех типов. Любой объект, объявленный как const, теперь можно раз.мещать в ПЗУ, в сегменте кода, защищать с помощью контроля доступа и т.д. - все это с целью гарантировать неиз.мешюсть после первоначальной инициализации. Такая защита, однако, не является обязательной, так как совре.менные систе.мы в осповно.м не в состоянии предохранить каждый константный объект от всех видов искаже1шя. Реализация все еще оставляет большую свободу выбора в том, как следует управлять константными объектами. Не возникает логической проблемы в случае, если сборщик .мусора или систе.ма баз данных изменит значение константного объекта (например, перенесет его на диск или обратно), при условии, что для пользователя объект будет выглядеть неизменившимся. 13.3.3. Ключевое слово mutable и приведение типов Снятие модификатора const все-таки вызывает некоторые возражения: во-первых, это разновидность приведения типов, во-вторых, не гарантируется правильная работа во всех случаях. Как можно написать класс типа XX (см. раздел 13.3.1), который не нуждался бы ни в приведении типов, ни в косвепно.м обращении, как в классе XXX? Томас Нго (Thomas Ngo) предложил, что должна быть возможность объявить член таким обра.зом, чтобы он никогда не считался константным, даже если является членом константного объекта. Данное предложение рассматривалось в комитете в течение нескольких лет, пока Джерри Шварц не нашел вариант, который всех устроил. Первоначально предлагалась нотация -const для выражения семантики никогда не может быть const . Даже некоторые сторонники самой концепции считали такую нотацию слишком неудачной, поэто.му в одобренном комитетом ANSI/ISO варианте фигурировало ключевое слово mutable: class XXX { int а; mutable int cnt; cnt никогда не может быть const public: int f{) const { cnt++; return a; } . . . XXX var; var.cnt изменяема (разумеется) const XXX cnst; cnst.cnt изменяема, так как XXX::cnt объявлен со словом mutable Концепция действительно снижает потребность в приведениях типов в реальных системах, но не до такой степени, как ожидалось. Дат Брюк и другие изучили много реальных программ для уточнения того, какие приведения использовались для снятия константности и от каких можно было бы отказаться за счет mutable. Исследование подтвердило вывод о том, что в общей ситуации от снятия const* нельзя отказаться (см. раздел 14.3.4) и что слово mutable позволяет избежать этого менее чем в половине случаев. Преимущества, которые дает mutable, похоже, сильно зависят от стиля программирования. В каких-то программах все приведения можно было бы заменить использование.м mutable, а в других - ни одного. Некоторые пользователи надеялись, что пересмотр концепции const и введение mutable открывают дорогу серьезным оптимизациям кода. Вряд ли это так. Основное достоинство - повышение ясности кода и увеличение числа объектов с предвычисленными значениями, которые можно поместить в ПЗУ, сегменты кода и т.д. 13.4. Статические функции-члены Статический (static) член-данные класса - это такой член, копия которого существует в единственном экземпляре, а не в каждо.м объекте. Значит, к статическому члену можно получить доступ, не ссылаясь ни на какой объект. Статические члены уменьшают число глобальных имен, делают очевидным, к какому классу логически принадлежат статические объекты; также обращение к ним осуществляется в соответствии с правилами контроля доступа. Это, безусловно, удобно для поставщиков библиотек, поскольку позволяет избежать загрязнения глобального пространства имен и, следовательно, упрощает написание кода библиотеки и делает более безопасной работу с несколькими библиотеками. Данные соображения применимы к функциям в той же мере, что и к объектам. Поставщик библиотеки обычно хочет сделать неглобальными имена функций. Я заметил, что для имитации статических функций-членов применялись непереносимые конструкции вроде { (X*) О) ->f {}. Этот прием - бо.мба замедленного действия, поскольку рано или поздно кто-то объявит функцию, вызываемую таким образом, виртуальной. Тогда последствия вызова будут непредсказуемы, ибо по нулевому адресу нет объекта класса X. Но даже если f {) невиртуальная функция,
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |