|
Программирование >> Арифметические и логические операции
protected: int p a; int& setA(const int& x); int getA(); public: /* }; */ a; так: Test t; t.a = 10; Вызов Test::setA() int r = t.a; Вызов Test::getA() Естественный способ - использовать шаблоны. Например, вот template<class A, class T, T& (A::*setter)(const T&), T (A::*getter)() class property }; Параметр A - класс, к которому принадлежат функции установки и чтения значения свойств (они передаются через аргументы шаблона setter и getter). Параметр T - тип самого свойства (например, int для предыдущего примера). На запись, которая используется для описания указателей на функции, стоит обратить внимание - известно, что многие программисты на C++ и не догадываются о том, что можно получить и использовать адрес функции-члена класса. Строго говоря, функция-член ничем не отличается от обычной, за исключением того, что ей требуется один неявный аргумент, который передается ей для определения того объекта класса, для которого она применяется (это указатель this). Таким образом, получение адреса для нее происходит аналогично, а вот использование указателя требует указать, какой конкретно объект используется. Запись A::*foo говорит о том, что это указатель на член класса A и для его использования потребуется объект этого класса. Теперь непосредственно весь класс целиком: template<class A, class T, T& (A::*setter)(const T&), T (A::*getter)() class property protected: A * ptr; public: property(A* p) : ptr(p) { } const T& operator= (const T& set) const assert(setter != 0); return (ptr->*setter)(set); operator T() const assert(getter != 0); return (ptr->*getter)(); Мы внесли тела функций внутрь класса для краткости (на самом деле, общая рекомендация никогда не захламлять определения классов реализациями функций - если нужен inline, то лучше его явно указать у тела подпрограммы, чем делать невозможным для чтения исходный текст. Приходится иногда видеть исходные тексты, в которых определения классов занимают по тысяче строк из-за того, что все методы были описаны внутри класса (как в Java). Это очень неудобно читать. Думаем, что идея предельно ясна. Использование указателей на функцию-член заключается как раз в строках вида: (ptr->*f)(); Указатель внутри свойства - это, конечно же, нехорошо. Тем не менее, без него не обойтись - указатель на объект нельзя будет передать в объявлении класса, только при создании объекта. Т.е., в параметры шаблона его никак не затолкать. Использовать класс property надо следующим образом: class Test property<Test, int, &Test::setA, &Test::getA> a; Test() : a(this) { } Компиляторы g++, msvc и bcc32 последних версий спокойно восприняли такое издевательство над собой. Тем не менее, более старые варианты этих же компиляторов могут ругаться на то, что используется незаконченный класс в параметрах шаблона, не понимать того, что берется адрес функций и т.д. Честно говоря, кажется что стандарту это не противоречит. Мы подошли к главному - зачем все это нужно. А вообще не нужно. Тяжело представить программиста, который решится на использование подобного шаблона - это же просто нонсенс. Опять же, не видно никаких преимуществ по сравнению с обычным вызовом нужных функций и видно только один недостаток - лишний указатель. Так что все, что здесь изложено, стоит рассматривать только как попытку продемонстрировать то, что можно получить при использовании шаблонов. На самом деле, теоретики расширения концепций очень часто забывают то, за что иногда действительно стоит уважать свойства. Это возможность сохранить через них объект и восстановить его (т.е., создать некоторое подобие persistent object). В принципе, добавить такую функциональность можно и в шаблон выше. Как? Это уже другой вопрос Таким образом, свойства как расширение языка ничего принципиально нового в него не добавляют. В принципе, возможна их реализация средствами самого языка, но использовать такую реализацию бессмысленно. Догадываться же, что такой подход возможен - полезно. Глава 14. Комментарии Плохое комментирование исходных текстов является одним из самых тяжелых заболеваний программ. Причем программисты зачастую путают хорошее комментирование и многословное . Согласитесь, комментарии вида: i = 10; Присваиваем значение 10 переменной i выглядят диковато. Тем не менее, их очень просто расставлять и, поэтому, этим часто злоупотребляют. Хороший комментарий не должен находится внутри основного текста подпрограммы. Комментарий должен располагаться перед заголовком функции; пояснять что и как делает подпрограмма, какие условия накладываются на входные данные и что от нее можно ожидать; визуально отделять тела функций друг от друга. Потому что при просмотре текста программы зачастую незаметно, где заканчивается одна и начинается другая подпрограмма. Желательно оформлять такие комментарии подобным образом: * function */ void function() Этот комментарий можно использовать в любом случае, даже если из названия подпрограммы понятно, что она делает - продублировать ее название не тяжелый труд. Единственное, из опыта следует, что не надо переводить название функции на русский язык; если уж писать комментарий, то он должен что-то добавлять к имеющейся информации. А так видно, что комментарий выполняет декоративную роль. Чем короче функция, тем лучше. Законченный кусочек программы, который и оформлен в виде независимого кода, значительно легче воспринимается, чем если бы он был внутри другой функции. Кроме того, всегда есть такая возможность, что этот коротенький кусочек потребуется в другом месте, а когда это требуется, программисты обычно поступают методом cut&paste, результаты которого очень трудно поддаются изменениям. Комментарии внутри тела функции должны быть только в тех местах, на которые обязательно надо обратить внимание. Это не значит, что надо расписывать алгоритм, реализуемый функцией, по строкам функции. Не надо считать того, кто будет читать ваш текст, за идиота, который не сможет самостоятельно сопоставить ваши действия с имеющимся алгоритмом. Алгоритм должен описываться перед заголовком в том самом большом комментарии, или должна быть дана ссылка на книгу, в которой этот алгоритм расписан. Комментарии внутри тела подпрограммы могут появляться только в том случае, если ее тело все-таки стало длинным. Такое случается, когда, например, пишется подпрограмма для разбора выражений, там от этого никуда особенно не уйдешь. Комментарии внутри таких подпрограмм, поясняющие действие какого-либо блока, не должны состоять из одной строки, а обязательно занимать несколько строчек для того, чтобы на них обращали внимание. Обычно это выглядит следующим образом: * Комментарий Тогда он смотрится не как обычный текст, а именно как нужное пояснение. Остальной текст, который желательно не комментировать, должен быть понятным. Названия переменных должны отображать их сущность и, по возможности, быть выполнены в едином стиле. Не надо считать, что короткое имя лучше чем длинное; если из названия короткого не следует сразу его смысл, то это имя следует изменить на более длинное. Кроме того, для C++ обычно используют области видимости для создания более понятных имен. Например, dictionary::Creator и index::Creator: внутри области видимости можно использовать просто Creator (что тоже достаточно удобно, потому что в коде, который имеет отношение к словарю и так ясно, какой Creator может быть без префикса), а снаружи используется нужный префикс, по которому смысл имени становится понятным. Кроме того, должны быть очень подробно прокомментированы интерфейсы. Иерархии классов должны быть широкими, а не глубокими. Все дело в подходе: вы описываете интерфейс, которому должны удовлетворять объекты, а после этого реализуете конкретные виды этих объектов. Обычно все, что видит пользователь - это только определение базового класса такой широкой иерархии классов, поэтому оно должно быть максимально понятно для него. Кстати, именно для возврата объектов, удовлетворяющих определенным интерфейсам, используют умные указатели. Еще хорошо бы снабдить каждый заголовочный файл кратким комментарием, поясняющим то, что в нем находится. Файлы реализации обычно смотрятся потом, когда по заголовочным файлам становится понятно, что и где находится, поэтому там такие комментарии возникают по мере необходимости. Таким образом, комментарии не должны затрагивать конкретно исходный текст программы, они все являются декларативными и должны давать возможность читателю понять суть работы программы не вдаваясь в детали. Все необходимые детали становятся понятными при внимательном изучении кода, поэтому комментарии рядом с кодом будут только отвлекать. Следовательно, надо предоставить читателю возможность отвлеченного ознакомления с кодом. Под этим подразумевается возможность удобного листания комментариев или распечаток программной документации. Подобную возможность обеспечивают программы автомати- зации создания программной документации. Таких программ достаточно много, для Java, например, существует JavaDoc, для C++ - doc++ и doxygen. Все они позволяют сделать по специального вида комментариям качественную документацию с большим количеством перекрестных ссылок и индексов. Вообще, хотелось бы немного отклониться от основной темы и пофилософствовать. Хороший комментарий сам по себе не появляется. Он является плодом тщательнейшей проработки алгоритма подпрограммы, анализа ситуации и прочее. Поэтому когда становится тяжело комментировать то, что вы хотите сделать, то это означает, скорее всего, то, что вы сами еще плохо сознаете, что хотите сделать. Из этого логичным образом вытекает то, что комментарии должны быть готовы до того, как вы начали программировать. Это предполагает, что вы сначала пишите документацию, а потом по ней строите исходный текст. Такой подход называется литературным программированием и автором данной концепции является сам Дональд Кнут (тот самый, который написал Искусство программирования и сделал TeX). У него даже есть программа, которая автоматизирует этот процесс, она называется Web. Изначально она была разработана для Pascalя, но потом появились варианты для других языков. Например, СWeb - это Web для языка Си. Используя Web вы описываете работу вашей программы, сначала самым общим образом. Затем описываете какие-то более специфические вещи и т.д. Кусочки кода появляются на самых глубоких уровнях вложенности этих описаний, что позволяет говорить о том, что и читатель, и вы, дойдете до реализации только после того, как поймете действие программы. Сам Web состоит из двух программ: для создания программной документации (получаются очень красивые отчеты) и создания исходного текста на целевом языке программирования. Кстати сказать, TeX написан на Webе, а документацию Web делает с использованием TeXа... как вы думаете, что из них раньше появилось? Web в основном применятся программистами, которые пишут сложные в алгоритмическом отношении программы. Сам факт присутствия документации, полученной из Web-исходника, упрощает ручное доказательство программ (если такое проводится). Тем не менее, общий подход к программированию должен быть именно такой: сначала думать, потом делать. При этом не надо мерить работу программиста по количеству строк им написанных.
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |