рефераты рефераты
Главная страница > Статья: Переход от С к С++  
Статья: Переход от С к С++
Главная страница
Банковское дело
Безопасность жизнедеятельности
Биология
Биржевое дело
Ботаника и сельское хоз-во
Бухгалтерский учет и аудит
География экономическая география
Геодезия
Геология
Госслужба
Гражданский процесс
Гражданское право
Иностранные языки лингвистика
Искусство
Историческая личность
История
История государства и права
История отечественного государства и права
История политичиских учений
История техники
История экономических учений
Биографии
Биология и химия
Издательское дело и полиграфия
Исторические личности
Краткое содержание произведений
Новейшая история политология
Остальные рефераты
Промышленность производство
психология педагогика
Коммуникации связь цифровые приборы и радиоэлектроника
Краеведение и этнография
Кулинария и продукты питания
Культура и искусство
Литература
Маркетинг реклама и торговля
Математика
Медицина
Реклама
Физика
Финансы
Химия
Экономическая теория
Юриспруденция
Юридическая наука
Компьютерные науки
Финансовые науки
Управленческие науки
Информатика программирование
Экономика
Архитектура
Банковское дело
Биржевое дело
Бухгалтерский учет и аудит
Валютные отношения
География
Кредитование
Инвестиции
Информатика
Кибернетика
Косметология
Наука и техника
Маркетинг
Культура и искусство
Менеджмент
Металлургия
Налогообложение
Предпринимательство
Радиоэлектроника
Страхование
Строительство
Схемотехника
Таможенная система
Сочинения по литературе и русскому языку
Теория организация
Теплотехника
Туризм
Управление
Форма поиска
Авторизация




 
Статистика
рефераты
Последние новости

Статья: Переход от С к С++

Статья: Переход от С к С++

Малышев Сергей Михайлович

Хочу сразу же сказать, что эта статья отнюдь не претендует на полные и безоговорочные рекомендации по переходу от С к С++. Тут даны лишь некоторые из очень многочисленных и может быть наиболее распространенные из них. Итак, к делу...

Для того чтобы освоиться с 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 и других стандартных типов имеются стандартные версии этих операторов.)

Страницы: 1, 2

рефераты
Новости