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