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

1 ... 25 26 27 [ 28 ] 29 30 31 ... 84


При этом происходит такое же нарушение правила одного определения, как и у фальсификатора. Однако если расположение данных объекта остается неизменным, этот способ сработает.

Преступник №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 показано, что код вне класса, не являющийся его другом, не в состоянии обратиться к закрытой функции по имени ни непосредственно (путем явного вызова), ни косвенно (через



1 ... 25 26 27 [ 28 ] 29 30 31 ... 84

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