50396 (Переход от С к С++)

2016-08-02СтудИзба

Описание файла

Документ из архива "Переход от С к С++", который расположен в категории "". Всё это находится в предмете "информатика" из , которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "остальное", в предмете "информатика, программирование" в общих файлах.

Онлайн просмотр документа "50396"

Текст из документа "50396"

Переход от С к С++

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

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

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

Более того, компилятор берет на себя заботу о том, какие версии операторов вызывать для разных переменных. Таким образом, вам нет необходимости беспокоиться о том, что первый читаемый или записываемый объект имеет тип int, а второй - Rational - с этим разберется компилятор!

Кроме того, считывание объектов происходит с использованием той же синтаксической формы, что и при записи. Поэтому нет необходимости помнить о том, что, если вы работаете не с указателем, важно не забыть взять адрес, а если имеете дело с указателем, следует убедиться, что вы не берете адрес. Пусть о таких деталях заботится компилятор C++. Это его дело. У вас же есть задачи посерьезнее.

И наконец, заметьте, что встроенные типы, подобные int, читаются и записываются совершенно аналогично типам, определенным пользователями, таким, например, как Rational. Попробуйте сделать то же самое, используя scanf/printf!

Ниже приводится пример того, как можно написать функцию для вывода класса рациональных чисел:

class Rational

{

public:

Rational(int numerator=0, int denominator=1);

int n, d; //числитель и знаменатель

friend ostream& operator"(ostream& s, const Rational& r);

};

ostream& operator"(ostream& s, const Rational& r)

{

s " r.n " '/' " r.d;

return s;

}

Эта версия operator" демонстрирует некоторые тонкости (притом весьма важные!). Например, она не является функцией-членом, а объект Rational передается operator" по ссылке const, а не как объект. Соответствующая функция ввода, operator", объявляется и реализуется аналогичным образом.

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

И наконец, поскольку классы библиотеки потоков ввода/вывода имеют конструкторы, а функции - нет, в редких случаях существенным будет порядок инициализации статических объектов, и стандартная библиотека С окажется более удобной просто потому, что вы можете ею пользоваться без опасений.

Контроль типов и расширяемость, предлагаемые классами и функциями библиотеки потоков ввода/вывода, являются более важными, чем это может показаться, - не стоит отвергать их только из-за того, что вы привыкли к .

Между прочим, это не опечатка - в названии данного правила действительно фигурирует , а не . Строго говоря, такого заголовка, как , не существует: Комитет по стандартам отказался от него в пользу названия при сокращении имен стандартных файлов заголовков, отсутствующих в библиотеке языка С.

Важно уяснить следующее: если (что весьма вероятно) ваш компилятор поддерживает как файл заголовков , так и , необходимо иметь в виду, что они слегка отличаются друг от друга. В частности, если вы включаете , элементы библиотеки потоков ввода/вывода весьма удобно расположены в пространстве имен std; включая , вы получаете те же элементы, но в глобальном пространстве имен. Их определение в нем может вести к конфликтам, предотвращению которых и должно было послужить введение понятия пространства имен. Кроме того, короче, чем .

Для многих это оказывается достаточным аргументом в пользу нового названия. Вот на этом пока и все. Если будут вопросы - пишите. По результатам вашего любопытства могут появиться новые статьи.

При написании данного текста активно использовалась книга Скотта Мейерса.

Список литературы

Scott Meyers Effective C++ Second Edition AWG 1998

Для подготовки данной работы были использованы материалы с сайта http://my-pc.jino.ru/

Свежие статьи
Популярно сейчас
Как Вы думаете, сколько людей до Вас делали точно такое же задание? 99% студентов выполняют точно такие же задания, как и их предшественники год назад. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
5161
Авторов
на СтудИзбе
438
Средний доход
с одного платного файла
Обучение Подробнее