Главная » Статьи » Информатика | [ Добавить статью ] |
Статья: Переход от С к С++Малышев Сергей Михайлович Хочу сразу же сказать, что эта статья отнюдь не претендует на полные и безоговорочные рекомендации по переходу от С к С++. Тут даны лишь некоторые из очень многочисленных и может быть наиболее распространенные из них. Итак, к делу... Для того чтобы освоиться с C++, необходимо некоторое время. Поскольку С является, по существу, подмножеством C++, все его старые "трюки" остаются в силе, но многие из них теряют свою значимость. Так, например, для программистов на C++ выражение "указатель на указатель" звучит немного забавно. Почему вместо указателя не была использована просто ссылка? С - достаточно простой язык. Макросы, указатели, структуры, массивы и функции - это почти все, что он в действительности предлагает. Каким бы сложным ни оказался алгоритм, его всегда можно реализовать, используя перечисленный набор средств. В C++ дело обстоит несколько иначе: наравне с макросами, указателями, структурами, массивами и функциями используются закрытые и защищенные члены классов, перегрузка функций, аргументы по умолчанию, конструкторы и деструкторы. Операции, определяемые пользователем, встроенные функции, ссылки, дружественные классы и функции, шаблоны, исключения, пространства имен и т. д. Очевидно, что более богатые средства проектирования предоставляют и более широкие возможности, а это в свою очередь, требует существенно иной культуры программирования. Столкнувшись с таким разнообразием выбора, многие теряются, продолжая крепко держаться за то, к чему они привыкли. По большей части в этом нет особого греха, но некоторые "привычки" из С идут вразрез с духом C++. От них просто необходимо избавиться! Давайте рассмотрим две наиболее частые и стойкие (на мой взгляд и собственный опыт) "привычки" из С - это использование директивы #define и функций scanf/printf. Предпочитайте const и inline использованию #define Этот правило лучше было бы назвать "Компилятор предпочтительнее препроцессора", поскольку #define зачастую вообще не относят к языку C++. В этом и заключается одна из проблем. Рассмотрим простой пример; попробуйте написать что-нибудь вроде: #define ASPECT_RATIO 1.653 Символическое обозначение может так и остаться неизвестным компилятору или быть удалено препроцессором, прежде чем код попадет в компилятор. Если это произойдет, то обозначение ASPECT_RATIO не окажется в таблице символов. Поэтому в ходе компиляции вы получите ошибку, связанную с использованием константы (в сообщении об ошибке будет сказано 1.653, а не ASPECT_RATIO). Это вызовет путаницу. Если файл заголовков писали не вы, а кто-либо другой, у вас не будет никакого представления о том, откуда взялось значение 1.653, и на поиски ответа вы потеряете много времени. Та же проблема может возникнуть и при отладке, поскольку обозначение, выбранное вами, будет отсутствовать в таблице символов. Указанная задача решается просто и быстро. Вместо использования макроса препроцессора определите константу: const double ASPECT_RATIO = 1.653; Однако есть два специальных случая, заслуживающих упоминания. Во-первых, при определении константных указателей могут возникнуть некоторые осложнения. Поскольку определения констант обычно выносятся в заголовочные файлы (где к ним получает доступ множество различных исходных файлов), важно, чтобы сам указатель был объявлен с const, в дополнение к объявлению const того, на что он указывает. Например, для определения в файле заголовков константной строки char* следует писать const дважды: const char* const constantString = "String is constant"; Во-вторых, иногда удобно определять некоторые константы, как относящиеся к конкретным классам, а это требует другого подхода. Для того чтобы ограничить область действия константы конкретным классом, необходимо сделать ее членом этого класса, а чтобы гарантировать, что существует только одна копия константы, требуется сделать ее статическим (static) членом класса: class GamePlayer { private: static const int NUM_TURNS = 5; // Объявление константы int scores[NUM_TURNS]; // Использование константы }; Остается еще одна небольшая проблема, поскольку все то, что вы видите выше - это объявление, а не определение NUM_TURNS. Если вам необходимо определить статические члены класса в файле реализации, то напишите следующее: сonst int GamePlayer::NUM_TURNS; // Обязательное объявление // находится в файле реализации Впрочем, терять сон из-за подобных пустяков не стоит. Если об определении забудете вы, то напомнит компоновщик. Старые компиляторы могут не поддерживать принятый здесь синтаксис, так как в более ранних версиях языка было запрещено задавать значения статических членов класса во время их объявления. Более того, инициализация в классе допускалась только для целых типов (таких как int, bool, char и пр.) и для констант. Если вышеприведенный синтаксис не работает, то начальное значение следует задавать в определении: class EngineeringConstants // Это находится в файле заголовка класса. { private: static const double FUDGE_FACTOR; }; А это находится в файле реализации класса: const double EngineeringConstants::FUDGE_FACTOR = 1.35; Единственное исключение обнаруживается тогда, когда для компиляции класса необходима константа. Например, при объявлении массива GamePlayer::scores в листинге, приведенном выше, в момент компиляции может потребоваться задание его размера. Для того чтобы работать с компилятором, ошибочно запрещающим инициализировать целые константы внутри класса, следует применять технику, которая иногда называется "трюком с перечислением". Она основана на том, что переменные перечисляемого типа можно использовать там, где ожидаются целые числа, поэтому GamePlayer определяют следующим образом: class GamePlayer { private: enum { NUM_TURNS = 5 }; //трюк с перечислением - делает из //NUM_TURNS символ со значением 5 int scores[NUM_TURNS]; //теперь нормально }; Если вы имеете дело не с примитивным компилятором, написанным до 1995 года и представляющим собой только исторический интерес, то считайте, что вам скорее всего повезло: необходимость использовать этот трюк вероятно отпадет сама собой. Вернемся к препроцессору. Другой частый случай неправильного использования директивы #define - создание макросов, которые выглядят как функции, но не обременены накладными расходами функционального вызова. Канонический пример - вычисление максимума двух значений: #define max(a, b) ((a)>(b)?(а):(b)) В этой небольшой строчке содержится так много недостатков, что даже не совсем понятно, с какого проще начать. Всякий раз, когда вы пишете макрос подобный этому, необходимо помнить, что все аргументы следует заключать в скобки. В противном случае у пользователей будут возникать серьезные проблемы с применением таких макросов в выражениях. Но, даже если вы все сделаете верно, посмотрите, какие странные (если не сказать - страшные) вещи могут при этом произойти: int а = 5, b = 0; mах(++а, b); //здесь переменная "а" - увеличивается дважды mах(++а, b+10); //а здесь - только 1 раз и это правильно Происходящее внутри mах зависит от того, что с чем сравнивается! Не верится? Проверьте сами. К счастью, вам нет нужды мириться с поведением, так сильно противоречащим привычной логике. Существует метод, позволяющий добиться такой же эффективности, как при использовании макросов. В таком случае обеспечиваются как предсказуемость поведения, так и контроль типов аргументов (что характерно для обычных функций). Этот результат достигается применением встраиваемых функций: inline int max(int a, int b) { return a > b ? a : b; } Новая версия max несколько отличается от предыдущей, поскольку она может работать только с целыми аргументами. Возникшую проблему удачно решает шаблон: template inline const Т& max(const Т& а, const T& b) { return а > b ? а : b; } Он генерирует целое семейство функций, каждая из которых берет два приводимых к одному типу объекта и возвращает ссылку (с модификатором const) на больший из двух объектов. Поскольку вам неизвестно, каким будет тип Т, для эффективности передача и возврат значения происходят по ссылке. Кстати говоря, прежде чем вы решите писать шаблон для какой-либо функции, подобной max, узнайте, не присутствует ли она уже в стандартной библиотеке. В случае с max вы можете воспользоваться плодами чужих усилий: max является частью стандартной библиотеки C++. Предпочитайте использованию О эти операторы sсanf() и printf()! Практически все формы обучения языку С и (увы) С++ начинаются с них. Да, они переносимы. Да, они эффективны. Да, вы уже даже знаете, как их нужно использовать. Но какой бы благоговейный восторг они ни вызывали, факт остается фактом: операторы sсanf() и printf() и им подобные далеки от совершенства. В частности, они не осуществляют контроль типа переменной и к тому же нерасширяемы. Поскольку контроль типов и расширяемость - краеугольные камни идеологии C++, то лучше всего с самого начала во всем опираться на них. Кроме того, семейство функций printf/scanf отделяет переменные, которые необходимо прочитать или записать, от форматирующей информации, управляющей записью и чтением. Неудивительно, что эти слабости функции printf/scanf - сила операторов " и ". int i; Rational r; // r является рациональным числом (класс Rational). cin " i " r; cout " i " r; Если этот код предназначен для компиляции, должны быть в наличии функции operator" и operator", которые могли бы работать с объектом типа Rational. Отсутствие данных функций является ошибкой. (Для int и других стандартных типов имеются стандартные версии этих операторов.) |
Чтобы скачать материал, пожалуйста, авторизуйтесь или зарегистрируйтесь! Это быстро ! )
Категории
Математика, алгебра, геометрия [1729] |
Книги (Это интересно) [351] |
Видеоуроки [26920] |
География [2660] |
Дополнительное образование [401] |
ЕГЭ/ГИА [266] |
Информатика [1187] |
История / обществознание [4663] |
Для Логопеда [500] |
Материалы для коррекц. классов [400] |
ОБЖ [558] |
Презентации [402] |
Для Психолога [514] |
Физическая культура [529] |
Черчение [121] |
Шаблоны презентаций [466] |
Для Библиотекаря [160] |
Праздники [419] |
Интересные Видеоролики [12] |
Английский язык [791] |
Иностранные языки (прочие) [461] |
Окружающий мир [873] |
Биология и экология [1643] |
Всем учителям [508] |
Для директора и завуча [1042] |
Дошкольное образование [1238] |
Искусство [804] |
Для Классного руководителя [543] |
Начальные классы [718] |
Основы религиозных культур [137] |
Программы и Софт [21] |
Родной язык [505] |
Русский язык и литература [3915] |
Технология [1000] |
Физика [1263] |
Химия [1297] |
Экономика [905] |
Астрономия [444] |
Писатели [113] |
Классный час [527] |
Шаблоны документов [85] |
Другое (Прочее) [412] |