15
низкоуровневые возможности С++, вы действуете, как суровый детектив с его сакраментальной
фразой: «Доверься мне — я знаю, что делаю». Компилятор закатывает глаза и безмолвно подчиняется.
С++ интригует своими явными противоречиями. Его гибкость легко превращается в главный источник
ошибок. За возможности его расширения не приходится расплачиваться скоростью или объемом кода.
Он элегантен в одних руках и опасен в других, прост и сложен одновременно. После нескольких лет
работы вы так и не можете решить, восхищаться им или проклинать. Да, настоящий знаток понимает
все концепции, лежащие в основе языка и склоняющие чашу весов в его пользу. Эти концепции не
видны с первого взгляда; чтобы понять их, необходимо в течение нескольких лет пытаться решать
совершенно разные задачи. Некоторые архитектурные парадигмы лучше всего соответствуют
конкретным языковым решениям. Их неправильное сочетание обернется хаосом, а правильное —
элегантностью.
Три великие идеи С++
О нетривиальном использовании C++ написано так много, что я даже не знаю, с чего начать. Вам
когда-нибудь приходилось видеть стереограммы? С первого взгляда они похожи на случайный узор, но
после медленного и внимательного разглядывания на них проявляется слон, спираль или что-нибудь
еще. Чтобы увидеть смысл в точках и пятнах, нужно рассматривать их в контексте объединяющей
темы. Именно здесь кроется одно из самых больших разочарований при изучении архитектуры и идиом
C++. Сначала кажется, что перед вами — огромная куча разрозненных приемов и ни одного правила
того, как ими пользоваться. Эта книга научит вас «видеть слона». Существует множество
классификаций нетривиальных аспектов C++, но я разделил их на несколько простых тем:
•
Косвенные обращения.
•
Гомоморфные иерархии классов.
•
Пространства памяти.
В основе каждой темы лежит конкретный синтаксис и средства C++, и их совместное применение
позволяет решать самые разные задачи. Существует много других приемов и принципов, которые тоже
стоило бы включить в эту книгу, но эти три категории помогают организовать очень большое
количество тем в логически стройную структуру.
В первой части приведен обзор многих важных аттракционов синтаксического цирка C++. Например,
многие программисты C++ не имеют большого опыта работы с перегруженными операторами и лишь
теоретически знают, как они применяются. Оказывается, большинство программистов никогда не
использует шаблоны или обработку исключений и лишь немногие умеют пользоваться потоками
ввода/вывода за рамками простейших обращений к объектам
cout
и
cin
. В части 1 стараюсь
выровнять уровни подготовки читателей, заполнить пробелы в ваших знаниях C++ и подготовиться к
игре. Часть 1 можно читать от корки до, бегло просматривать или пропускать целые разделы в
зависимости от того, насколько хорошо вы знакомы с нюансами C++.
Термин косвенное обращение (indirection) относится к разным конкретным темам, однако везде
используется одна и та же концепция: клиентский объект обращается с запросом к другому объекту,
который, в свою очередь, поручает работу третьему объекту. Косвенность связана со средним объектом
в цепочке. Иногда годится слышать, что это определение почти совпадает с определением
делегирования (delegation),
одного
из
краеугольных
камней
объектно-ориентированного
программирования. Тем не менее, в C++ идиомы, используемые с этой концепцией, и ее языковая
поддержка выходят далеко за рамки того, что считается делегированием
в других языках. В этой книге
часто используется термин указатель (pointer); вы встретите его в каждой главе. Указатели C++
способны на многое. Они могут определить, где в памяти, на диске или в сети находится объект, на
который они ссылаются; когда он уничтожается; изменяется ли он или доступен только для чтения; и
даже то, существует ли объект или просто представляет собой некую область в абстрактном
пространстве памяти — и все это происходит без активного участия самого объекта, который может
ничего не знать об этих низкоуровневых операциях. Возможности, что и говорить, впечатляющие,
однако они основаны на нескольких очень простых идиомах.
О проектировании иерархии классов говорили все кому не лень — одни по делу, другие болтали об
«имитации объектов реального мира». Большинство аргументов в равной степени относится к любому
объектно-ориентированному языку, и я вовсе не намерен захламлять книгу по C++ своими личными
16
взглядами на объектно-ориентированный дизайн. Тем не менее, один конкретный тип наследования —
гомоморфное наследование (homomorphic derivation) — оказывается исключительно полезным в
сочетании со специфическими средствами C++. В гомоморфной иерархии все производные классы
получают свой открытый интерфейс от некоторого базового класса-предка. Как правило, «мать всех
базовых классов» девственно чиста — она не содержит ни одной переменной, а все ее функции
являются чисто виртуальными. В C++ с этой концепцией ассоциируются многие полезные идиомы
проектирования и программирования.
За концепцией пространства памяти (memory space) кроется нечто большее, чем обычное управление
памятью. Перегружая в C++ операторы
new
и
delete
, вы определяете, где создаются объекты и как
они уничтожаются. Кроме того, можно создавать абстрактные коллекции, в которых не всегда понятно,
с чем вы имеете дело — с настоящим объектом или с абстракцией. На горизонте уже видны контуры
новых распределенных объектно-ориентированных структур, разработанных такими фирмами, как
Microsoft, Apple и Taligent. Правда, вам придется пересмотреть некоторые базовые представления о
том, где находятся объекты и как они перемещаются в другое место — все эти темы я выделил в
категорию пространств памяти. Пространства памяти позволяют определить тип объекта во время
выполнения программы — возможность, которой до обидного не хватает в C++. Конечно, мы
поговорим и об управлении памятью, но этим дело не ограничится.
Как читать эту книгу
Перед вами — не руководство с готовыми рецептами для конкретных ситуаций. Скорее это сборник
творческих идей и головоломок. Если к концу книги вы почувствуете, что ваш арсенал приемов
программирования на C++ расширился, значит, я достиг своей цели, а учить вас, когда и как
пользоваться этими приемами, я не стану.
Материал каждой отдельной главы невозможно в полной мере понять без предварительного знакомства
со всеми остальными главами. И все же я приложил максимум усилий, чтобы материал любой главы
был полезен немедленно после знакомства с ней и чтобы главы логически следовали друг за другом, а
наш воображаемый слон вырисовывался постепенно — бивни, уши, хобот и т. д. После прочтения
книга может пригодиться в качестве справочника — что-то вроде личной и очень краткой
энциклопедии приемов программирования и идиом C++.
За многие годы изучения и использования C++ я узнал, что даже у опытных программистов в
познаниях встречаются пробелы; в оставшемся материале части я постараюсь выровнять уровень
подготовки всех читателей. Это не вступительное описание языка, а скорее краткая сводка тем,
которые будут использованы в последующих главах. В главе 2 мы стремительно пробежимся по
некоторым особенностям языка. Глава 3 посвящена шаблонам — постепенно эта тема становится все
более важной, поскольку шаблоны поддерживаются во все большем числе компиляторов. В главе 4
рассматривается обработка исключений на основе рекомендованного стандарта ANSI и приводится
пара замечаний о нестандартных исключениях, встречающихся в реальном мире.
Часть 2 посвящена разным типам указателей — от глупых до гениальных. На этом фундаменте
построена вся книга, и я уверен, что эти сведения будут полезны любому читателю.
В части 3 рассматриваются структура и реализация типов и иерархий классов в C++. Основное
внимание уделено одному из частных случаев — гомоморфным иерархиям классов. Заодно мы
поговорим об объектах классов, представителях и других любопытных темах. Большинству читателей
стоит прочитать третью часть от начала до конца, но никто не запрещает вам просмотреть ее и
отобрать темы по своему вкусу. И хотя вы будете полагать, что знаете об указателях все на свете, они
совершенно неожиданно снова возникнут в контексте гомоморфных иерархий.
В части 4 нас поджидает самая ужасная тема C++ — управление памятью. Уровень изложения
меняется от примитивного до нормального и сверхсложного, но основное внимание уделяется тем
проблемам, которые могут возникнуть при программировании на C++, и их возможным решениям на
базе разных языковых средств. Лично я считаю, что начальные главы этой части абсолютно
необходимы для счастливой и полноценной жизни в C++, но если вас, допустим, совершенно не
интересует процесс сборки мусора — оставьте последнюю пару глав и займитесь чем-нибудь более
полезным для общества.
Do'stlaringiz bilan baham: |