Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Сабуров С.В. - Язык программирования C и C++ - 2006

.pdf
Скачиваний:
312
Добавлен:
13.08.2013
Размер:
1.42 Mб
Скачать

Трюки программирования

Трюки программирования

Правило «право!лево»

Существенный принцип анализа сложных синтаксических конструкций языка, вроде «указатель на функцию, возвращающую указатель на массив из трёх указателей на функции, возвращающие значение int» чётко формализован в виде правила «право лево». Всё предельно просто. Имеем:

() — функция, возвращающая...

[] — массив из...

* — указатель на...

Первым делом находим имя, от которого и будем плясать.

Следующий шаг — шаг вправо. Что там у нас справа? Если (), то говорим, что «Имя есть функция, возвращающая...». (Если между скобок что то есть, то «Имя есть функция, принимающая то, что между скобок, и возвращающая...»). Если там [], то «Имя есть массив из...». И подобным вот образом мы идём вправо до тех пор, пока не дойдём до конца объявления или правой «)» скобки. Тут тормозим...

...и начинаем танцевать влево. Что у нас слева? Если это что то не из приведенного выше (то есть не (), [], *), то попросту добавляем к уже существующей расшифровке. Если же там что то из этих трёх символов, то добавляем то, что написано выше. И так танцуем до тех пор, пока не дотанцуем до конца (точнее — начала объявления) или левой «(» скобки. Если дошли до начала, то всё готово. А если дошли до «(», то по уже означенной итеративности переходим к шагу «Пляски вправо» и продолжаем.

Пример:

int (*(*(*fptr)())[3])();

Находим имя и записываем «fptr есть...».

Шаг вправо, но там «)», потому идём влево:

int (*(*(*fun)())[3])();

и получаем «fptr есть указатель на...».

367

Трюки программирования

Продолжаем ехать влево, но тут «(». Идём вправо:

int (*(*(*fun)())[3])();

получаем «fptr есть указатель на функцию, возвращающую...»

Снова «, опять влево. Получаем:

int (*(*(*fun)())[3])();

«fptr есть указатель на функцию, возвращающую указатель на...»

Слева опять «(», идём вправо. Получаем:

int (*(*(*fun)())[3])();

«fptr есть указатель на функцию, возвращающую указатель на массив из трёх...» И снова справа «)», отправляемся влево. Получаем:

int (*(*(*fun)())[3])();

«fptr есть указатель на функцию, возвращающую указатель на массив из трёх указателей на...» Снова разворот вправо по причине «(». Получаем:

int (*(*(*fun)())[3])();

«fptr есть указатель на функцию, возвращающую указатель на массив из трёх указателей на функции, возвращающие...» Тут конец описания, поехали влево и получили окончательную расшифровку:

int (*(*(*fun)())[3])();

«fptr есть указатель на функцию, возвращающую указатель на массив из трёх указателей на функции, возвращающие int».

Именно то, чего мы хотели.

STLport 4.0

В июле 2000 года наконец то вышла новая версия библиотеки STLport 4.0. Для тех, кто еще не в курсе, что это такое, объясним: это свободно распространяемая реализация стандартной библиотеки шаблонов для множества различных компиляторов и операционных систем. Помимо всего прочего, STLport доступен не только для современных компиляторов, более или менее удовлетворяющих стандарту языка, но и для некоторых старых компиляторов, в частности Borland C++ 5.02 или MS Visual C++ 4.0.

368

Трюки программирования

Четвертая версия STLport отличается от предыдущей главным образом тем, что теперь в нее входит полная поддержка потоков (ранее приходилось использовать потоки из библиотеки, поставляемой с конкретным компилятором). Реализация потоков взята из SGI (как, впрочем, и весь STLport). Вообще, STLport начал развиваться как попытка перенести известную библиотеку SGI STL на gcc и sun cc. Таким образом, с выходом четвертой версии, STLport стал полноценной библиотекой, соответствующей стандарту языка, во всяком случае, у него появились претензии на это.

Понятно, что применение одной и той же библиотеки на разных платформах, это уже большой плюс — потому что никогда точно заранее не известно, что, где и как будет плохо себя вести. Можно только лишь гарантировать, что программа, при переносе с одного компилятора на другой, все таки будет себя плохо вести даже в том случае, если скомпилируется. Использование одной библиотеки шаблонов очень сильно увеличивает шансы на то, что не будет проблем тогда, когда программист увидит отсутствие в STL нового компилятора какого нибудь контейнера. К примеру, в g++ stl 3 нет std::wstring. То есть, шаблон std::basic_string есть, и std::string является его инстанционированием на char, но попытка подставить туда же wchar_t ни к чему хорошему не приведет (в частности, из за того, что в методе c_str() есть исключительная строчка вида return "").

Но и кроме единых исходных текстов у STLport есть еще несколько интересных возможностей и особенностей. Во первых, это debug mode, при котором проверяются все условия, которые только возможны. В частности, в этом режиме при попытке работать с неинициализированным итератором будет выдано соответствующее ругательство. Согласитесь, это удобно.

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

369

Трюки программирования

В третьих, поддержка многопоточности. То есть, STLport можно безопасно использовать в программах, у которых более одного потока выполнения. Это досталось STLport еще от SGI STL, в которой очень много внимания уделялось именно безопасности использования.

Помимо того, если вдруг возникли какие то проблемы с STL, то можно попытаться взять STLport — быть может, проблем станет поменьше.

Новый язык программирования от Microsoft: C#

Фирма Microsoft создала новый язык программирования, сделанный на основе Си и C++ и Java, который она назвала C# (C sharp).

Чтобы реально посмотреть язык программирования, возьмем программу «Hello, world!» из C# Language Reference:

using System; class Hello

{

static void Main() { Console.WriteLine("Hello, world");

}

}

Это очень сильно напоминает Java. Таким образом, что имеется в наличии:

Убрали селектор >, впрочем это, возможно и правильно: и «точка» и «стрелка» выполняют, в принципе, одни и те же функции с точки зрения ООП, так что в этом есть намеки на концептуальность. В принципе, это стало вероятным благодаря тому, что в C# есть типы «значения» (такие как, int, char, структуры и перечисления) и типы «ссылки», которыми являются объекты классов, массивы.

Точно так же, как и в Java, перенесли метод main внутрь класса.

Точно так же, как и в Java, в программах на C# теперь нет необходимости в декларациях без дефиниций, т.е. компилятор многопроходный.

370

Трюки программирования

Конечно же, не смогли обойтись без автоматического сборщика мусора, так что в C#, так же как и в Java, не требуется беспокоиться об удалении памяти из под объектов. Тем не менее, введена такая возможность, под названием «unsafe code», используя которую можно работать с указателями напрямую.

Появился тип object с понятными последствиями: все типы (включая типы «значения») являются потомками object.

Между bool и integer нет кастинга по умолчанию. Тип char — это Unicode символ (так же, как и в Java).

Есть поддержка настоящих многомерных массивов (а не массивов массивов).

Вотличие от Java, в C# выжил оператор goto.

Появился оригинальный оператор foreach: static void WriteList(ArrayList list) {

foreach (object o in list) Console.WriteLine(o);

}

который позволяет обойти контейнер.

Есть еще два интересных оператора: checked и unchecked. Они позволяют выполнять арифметические операции с проверкой на переполнение и без него.

Существует поддержка многопоточности при помощи оператора lock.

Отсутствует множественное наследование — вместо него, как и в Java, добавлена поддержка интерфейсов. Кстати сказать, структуры теперь совсем не тоже самое, что и классы. В частности, структуры не могут быть наследованы.

Добавлена поддержка свойств (property).

На языковом уровне введена поддержка отклика на события.

Введены определяемые пользователями атрибуты для поддержки систем автодокументации.

Кардинальное отличие от Java — наличие компилятора в машинный код. То есть, можно предположить, что программы на

371

Трюки программирования

C# будут выполняться несколько быстрее, чем написанные на

Java.

Вообще, можно говорить о том, что Microsoft учла традиционные нарекания в сторону Java в своем новом языке. В частности, оставлена от C++ перегрузка операторов.

Компания Microsoft утверждает, что создала язык для написания переносимых web приложений и старается всячески показать свою собственную активность в этом направлении. В частности, компания Microsoft направила запрос на стандартизацию C#.

В принципе, ясно, зачем все это нужно. Компании Microsoft, несомненно, понадобился свой язык программирования такого же класса, как и Java. Пускать же Java к себе в Microsoft никто не намеревался, вот и получился C#. Понятно, что в данном случае язык программирования сам по себе представляет достаточно малую ценность, в силу того что Java хороша своей переносимостью, а переносимость ей гарантирует мощная и обширная стандартная библиотека, употребляя которую нет надобности вызывать какие то системно или аппаратно зависимые фрагменты кода. Поэтому на текущий момент ничего определенного сказать о судьбе C# нельзя — хотя бы потому, что у него пока что нет подобной библиотеки.

Все же, в ближайшие несколько лет будет очень интересно следить за развитием C# и Java. В принципе, еще недавно представлялось, что уже невозможно вытеснить Java из своей ниши инструмента для относительно простого создания переносимых приложений, но вот, Microsoft решилась на эту попытку. Учитывая то, что в свое время было очевидно главенство Netscape на рынке броузеров, ожидать можно всего.

C++ Builder

В первую очередь оговоримся, что здесь мы будем рассматривать C++ Builder именно как «builder», т.е. программный инструмент класса RAD (Rapid Application Development, быстрое создание приложений) и, в общем то, большая часть здесь написанного в одинаковой степени применимо ко всем подобным средствам.

372

Трюки программирования

Итак, C++ Builder облегчает процесс создания программ для ОС Windows с графическим интерфейсом пользователя. При его помощи одинаково просто создать диалог с тремя кнопочками «Yes», «No», «Cancel» или окно текстового WYSIWYG редактора с возможностью выбора шрифтов, форматирования, работы с файлами формата rtf. При этом C++ Builder автоматически создает исходный текст для пользовательского интерфейса: создает новые классы, объекты, добавляет необходимые переменные и функции. После всего этого «рисование» пользовательского интерфейса превращается, буквально, в наслаждение для эстетов: сюда добавим градиент, здесь цвет изменим, тут шрифт поменяем, а сюда мы поместим картинку.

После того, как вся эта красота набросана, наступает менее привлекательная работа — написание функциональной части. Тут C++ Builder ничем помочь не может, все приходиться делать по старинке, позабыв про «манипулятор мышь» и касаясь исключительно клавиатуры.

Итог?.. Как обычно: красота неписанная на экране. Этих программ, которые рисовали эстетствующие программисты в настоящее время видимо невидимо, ими можно любоваться, распечатывать картинки с экрана и делать из них художественные галереи...

Что же тут плохого? Ничего, если не считать того, что при таком подходе к программированию создание программного продукта начинает идти не от его «внутренностей» (функционального наполнения), а от пользовательского интерфейса и в итоге получается, если «наполнение» достаточно сложное (сложнее передачи текста от одного элемента пользовательского интерфейса другому), то оно становится не только системнозависимым, но и компиляторозависимым, что уж абсолютно неприятно.

Кроме того, простота «рисования» пользовательского интерфейса, а, вернее, ненаказуемость (например, объемным программированием) использования всевозможных сложных компонентов скрывает в себе некоторые опасности. Связано это с тем, что создание удобного пользовательского интерфейса это задача сама по себе довольно трудная и требующая особенного образования. При этом всякий уважающий себя программист

373

Трюки программирования

всегда уверен в том, что уж он то точно сможет сделать пользовательский интерфейс предельно удобным и красивым.

Почему настолько разительно отличаются домашние страницы и страницы профессиональных web дизайнеров? Вследствие того что последние имеют очень много узкоспециализированных знаний о восприятии человеком информации на экране монитора и, благодаря этому, могут разместить информацию не «красиво», а так, как это будет удобным. То же самое и с пользовательским интерфейсом — вопрос о том, как должна выглядеть конкретная кнопка и в каком месте она должна находиться не так прост, как кажется. Вообще, дизайнер пользовательского интерфейса это совершенно исключительная профессия, которая, к сожалению, у нас еще не распространена.

Тот факт, что функциональное наполнение становится зависимым от используемой библиотеки пользовательского интерфейса, просто абсурден. Подставьте в предыдущее предложение взамен «функционального наполнения» конкретный продукт, и вы уясните о чем мы хотим сказать: «расчет химических реакций», «анализ текста» и т.д.

Помимо того, сама библиотека пользовательского интерфейса в C++ Builder довольно оригинальна. Это VCL (Visual Component Library), всецело позаимствованная из Delphi, т.е. написанная на Паскале. По Паскалевским исходникам автоматически создаются заголовочные файлы, которые в дальнейшем включаются в файлы, написанные на C++. Необходимо сказать, что классы, которые представляют из себя VCL компоненты это не обычные C++ классы; для совместимости с Delphi их пришлось отчасти изменить (скажем, VCL классы не могут участвовать во множественном наследовании); т.е. в С++ Builder есть два вида классов: обычные C++ классы и VCL классы.

Помимо всего прочего, C++ Builder еще и вреден. Вследствие того что очень много начинающих программистов используют его, расхваливают за то, что при его помощи все так просто делается и не подозревают о том, что это, на самом деле, не правильно. Ведь область применения C++ Builder, в общем то, достаточно хорошо определена — это клиентские части для каких либо БД. В нем все есть для этого: быстрое создание

374

Трюки программирования

интерфейса, генераторы отчетов, средства сопряжения с таблицами. Но все, что выходит за границы данной области, извините, надо писать «как обычно».

Связано это с тем, что, создание программ, которые в принципе не переносимы — это просто издевательство над идеями C++. Ясно, что написать программу, которая компилируется несколькими компиляторами это в принципе сложно, но сделать так, чтобы это было ко всему прочему и невозможно, до чрезвычайности неприлично. Всякая программа уже должна изначально (и это даже не вопрос для обсуждения) иметь очень отчетливую грань между своим «содержанием» и «пользовательским интерфейсом», между которыми должна быть некоторая прослойка (программный интерфейс) при помощи которой «пользовательский интерфейс» общается с «содержанием». В подобном виде можно сделать хоть десяток пользовательских интерфейсов на различных платформах, очень просто «прикрутить» COM или CORBA, написать соответствующий этой же программе CGI скрипт и т.д. В общем, немало достоинств по сравнению с жестким внедрением библиотеки пользовательского интерфейса внутрь программы против одного преимущества обратного подхода: отсутствие необходимости думать перед тем, как начать программировать.

Необходимо сказать, что C++ Builder или Delphi такой популярности как у нас, за границей не имеют. Там эту же нишу прочно занял Visual Basic, что достаточно точно говорит об области применения RAD средств.

C++ Builder буквально навязывает программисту свой собственный стиль программирования, при котором, даже при особом желании, перейти с C++ Builder на что то другое уже не предоставляется возможным.

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

375

Трюки программирования

Применение «умных» указателей

Принципы использования «умных» указателей знакомы любому программисту на C++. Идея предельно проста: взамен того, чтобы пользоваться объектами некоторого класса, указателями на эти объекты или ссылками, определяется новый тип для которого переопределен селектор >, что позволяет использовать объекты такого типа в качестве ссылок на реальные объекты. На всякий случай, приведем следующий пример:

class A { public:

void method();

};

class APtr { protected:

A* a; public:

APtr();

~APtr();

A* operator >();

};

inline APtr::APtr() : a(new A) { }

inline APtr::~APtr()

{

delete a;

}

inline A* APtr::operator >()

{

return a;

}

Теперь для объекта, определенного как

APtr: aptr;

можно использовать следующую форму доступа к члену a: aptr >method();

376

Соседние файлы в предмете Программирование на C++