|
Программирование >> Обобщенные обратные вызовы
При этом происходит такое же нарушение правила одного определения, как и у фальсификатора. Однако если расположение данных объекта остается неизменным, этот способ сработает. Преступник №3: мошенник Принцип работы мошенника - в подмене понятий. Пример 15-3: попытка имитации размещения данных объекта class BaitAndswitch { в надежде, что размещение данных в public: этом классе будет тем же, что и в х int notSoPrivate; void fC х& X ) { Creinterpret cast <BaitAndSwitch&>(x)).notSoPrivate = 2; } Злобный смех Этот человек - мошенник. Хорошенько его запомните! Его реклама рассчитана только на то, чтоб затащить вас в магазин, а уж там он обязательно ухитриться продать вам совершенно не ту вещь, о которой шла речь в рекламе, да еще и в несколько раз дороже, чем в любом другом месте. Конечно, то, что делает мошенник, - не законно. Код из примера 15-3 не законен по двум причинам. Нет гарантии, что размещение объектов х и BaitAndswitch в памяти будет одинаковым - хотя на практике это обычно так и есть. Результат reinterpret cast не определен, хотя большинство компиляторов сделают именно то, чего от них хочет мошенник. В конце концов, говоря волшебное слово reinterpret cast, вы заставляете компилятор поверить вам и закрыть глаза на подготавливаемое вами мошенничество. Но вопрос не исчерпывался только обманными способами. Если помните, нас спрашивали и о том, как добиться интересующего результата совершенно корректно и переносимо с точки зрения стандарта. Увы, крупные преступники имеют весьма респектабельный вид и большие счета в банках, так что их очень трудно схватить за руку. Персона грата №4: адвокат Многие из нас недаром больше боятся хорошо одетых и улыбающихся адвокатов, чем (других) преступников. Рассмотрим следующий код. Пример 15-4: проныра-законник namespace { struct Y {}; tempi ateo void x::f( const Y& ) { private = 2; Злобный смех void TestO { X x; cout X.ValueO endl; выводит 1 x.fC YO ); cout x.valueO endl ; выводит 2 Этот человек - адвокат, который знает все лазейки. Его невозможно поймать, поскольку он слишком осторожен, чтобы нарушить букву закона, при этом нарушая его дух. Запомните и избегайте таких неджентльменов. Как бы мне ни хотелось сказать Конечно, то, что делает адвокат, - не законно , увы, я не могу этого сделать, поскольку все сделанное в последнем примере законно. Почему? В примере !5-4 использован тот факт, что у х есть шаблон функции-члена. Приведенный код соответствует стандарту, так что последний гарантирует, что он будет работать так, как ожидается. Причины здесь две. Можно с п е ц и а л и 3 и р о в ат ь шаблон-член для любого типа. Единственное место, где могла бы проявиться ошибка, -- это две разные специализации для одного и того же типа, что нарушало бы правило одного определения. Но и тут все оказывается в порядке: Код использует гарантированно уникальный тип, поскольку он находится в безымянном пространстве имен. Следовательно, гарантируется, что приведенный код корректен и не будет конфликтовать ни с какой другой специализацией. Не нарушай Остается только один вопрос. 3. Подумайте, не является ли это дырой в механизме управления правами доступа в С++ (а следовательно, дырой в инкапсуляции С++)? Данный пример демонстрирует интересное взаимодействие двух возможностей С+ + : модель управления правами доступа и модель шаблонов. В результате оказывается, что шаблоны-члены неявно нарушают инкапсуляцию в том смысле, что они предоставляют переносимую возможность обхода механизма управления доступом к классу. В действительности не это является проблемой. Проблема в защите от дурака , т.е. от непреднамеренного случайного использования (с чем язык справляется очень хорошо) и в защите от намеренного злоупотребления (против чего эффективная зашита невозможна). В конечном итоге, если профаммист намерен нарушить систему защиты, он найдет способы это сделать, как продемонстрировано в примерах с 15-1 по 15-3. Правильный ответ на вопрос - не делайте этого! По общему признанию, бывают ситуации, когда хочется иметь быстрый способ временного обхода механизма контроля прав доступа, например, при отладке... но это не просто плохая привычка, которая может привести к использованию таких методов в окончательной версии кода. В конечном итоге это приведет к более серьезным неприятностям. У Рекомендация Никогда не коверкайте язык программирования. Например, не пьггайтесь нарушить инкапсуляцию путем копирования определения класса с добавлением друга или при помощи локального инстанцирования шаблона функции-члена. Задача 16. Крепко закрыт? Сложность: 5 в какой степени закрыты закрытые частм класса в С++? Благодаря этой задаче мы увидим, что закрытые имена определенно недоступны для внешнего кода, не являющегося дружеским, но, тем не менее, имеются некоторые пути, по которым до них можно дотянуться - как хорошо известные, так и не очень. Вопрос ДЛЯ профессионала 1. Ответьте быстро. Предположим, что функции Twice определены в другой единице трансляции, подключаемой при компоновке. Будет ли приведенная далее программа на С++ компилироваться и корректно выполняться? Если нет, то почему? Если да, то что она даст на выходе? Twice(x) возвращает 2*х class Calc { publi с: double TwiceC double d ); private: int TwiceC int i ); std::complex<float> TwiceC std::complex<f1oat> с ); int mainO { calc c; return c.Twice С 21 ); Решение в основе решения лежит упомянутый вопрос -- до какой степени в действительности закрыты закрытые части класса, такие как функции Twice в данной задаче? Доступность Главное, что требуется осознать, - .это то, что private так же, как publiс и protected, представляют собой спецификаторы доступа, т.е. они управляют тем, в какой степени другой код может обрашаться к именам членов - и не более того. Процитируем стандарт [С++03]. Член класса может быть: закрытым (private), т.е. его имя может использоваться только членами и друзьями класса, в котором он объявлен; защищенным (protected), т.е. его имя может использоваться только членами и друзьями класса, в котором он объявлен, а также членами и друзьями классов, производных от данного; открытым (public), т.е. его имя может использоваться везде, без каких-либо ограничений доступа. Это базовый материал, но давайте для полноты рассмотрим простой пример, демонстрирующий, как обеспечивается проверка прав доступа, и убедимся, что нет стандартных путей, позволяющих обойти этот механизм. В примере 16-1 показано, что код вне класса, не являющийся его другом, не в состоянии обратиться к закрытой функции по имени ни непосредственно (путем явного вызова), ни косвенно (через
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |