Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [ 29 ] 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

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



указатель на функцию), поскольку имя функции не может быть использовано никаким образом, включая получение адреса функции:

пример 16-1: доступ к No::satisfaction невозможен

class NO { private:

virtual void Satisfaction() { }

int main() { No no;

no.Satisfaction C); ошибка

typedef void (no::*PMember)(); pMember p = too::satisfaction ; ошибка return (no.*p)(); иу-ну...

Обратите внимание на то, что в этом примере рассматривается виртуальная функция, и права доступа распространяются и на нее. Закрытый член, представляющий собой виртуальную функцию, может быть перекрыт в любом производном классе, но доступ к нему из производного класса невозможен. Точнее, производный класс может перекрыть любую виртуальную функцию своей собственной функцией с тем же именем, но не может вызвать или каким-либо иным образом использовать имя закрытой виртуальной функции из базового класса, например:

Пример 16-1, продолжение: производный класс может перекрыть закрытый виртуальный член, но не

может к нему обратиться

ass Derived : publiс No { vi rtual void satisfaction() { корректно NO::Sati sfacti on () ; Ошибка

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

• Закрытое (private) имя члена доступно только для других членов и друзей.

Если бы на этом все и заканчивалось, это была бы самая короткая (и бессмысленная) задача в книге. Но, как вы понимаете, на доступности имени наш рассказ не заканчивается.

Видимость

Ключевое слово private управляет доступностью членов, но есть еще одна связанная с ней концепция, которую часто путают с доступностью, а именно видимость. Вернемся к коду, приведенному в вопросе задачи: будет ли он компилироваться и корректно выполняться?

Краткий ответ - нет. В приведенном виде программа некорректна и компилироваться не будет по двум причинам. Первая причина - довольно очевидная ошибка.

Пример 16-2 (код из условия задачи)

Twice(x) возвращает 2* х

class calс { public:

Мошеннические способы наподобие подмены ключевого слова pri vate словом publ i с (см. задачу 15) мы не рассматриваем.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [ 29 ] 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85