Средства C, которых рекомендуется избегать 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Средства C, которых рекомендуется избегать



По замечанию Страуструпа, «чем лучше вы знаете C, тем труднее вам будет избежать программирования на C++ в стиле C, теряя при этом потенциальные преимущества C++». В связи с этим он даёт следующий набор рекомендаций для программистов на C, чтобы в полной мере воспользоваться преимуществами C++:

  • Не использовать макроопределения #define. Для объявления констант применять const, групп констант (перечислений) — enum, для прямого включения функций — inline, для определения семейств функций или типов — template.
  • Не использовать предварительные объявления переменных. Объявлять переменные в блоке, где они реально используются, всегда совмещая объявление с инициализацией.
  • Отказаться от использования malloc() в пользу оператора new, от realloc() — в пользу типа vector.
  • Избегать бестиповых указателей, арифметики указателей, неявных приведений типов, объединений, за исключением, возможно, низкоуровневого кода. Использовать «новые» преобразования типов, как более точно выражающие действительные намерения программиста и более безопасные.
  • Свести к минимуму использование массивов символов и строк в стиле C, заменив их на типы string и vector из STL. Вообще не стремиться создавать собственные реализации того, что уже имеется в стандартной библиотеке.

Дальнейшее развитие

Текущий стандарт языка ISO/IEC 14882:2003(E) был принят в 2003 году. Неофициально его обозначают как C++03. Следующая версия стандарта, опубликованная 1 сентября 2011 года, имеет неофициальное обозначение C++11.

Общие направления развития C++

По мнению автора языка Бьёрна Страуструпа[11][12][13], говоря о дальнейшем развитии и перспективах языка, можно выделить следующее:

  • В основном дальнейшее развитие языка будет идти по пути внесения дополнений в стандартную библиотеку. Одним из основных источников этих дополнений является известная библиотека boost.
  • Изменения в ядре языка не должны приводить к снижению уже достигнутой эффективности C++. С точки зрения Страуструпа, предпочтительнее внесение в ядро нескольких серьёзных больших изменений, чем множества мелких правок.
  • Базовыми направлениями развития C++ на ближайшее время является расширение возможностей и доработка средств обобщённого программирования, стандартизация механизмов параллельной обработки, а также доработка средств безопасного программирования, таких как различные проверки и безопасные преобразования типов, проверка условий и так далее.
  • В целом C++ спроектирован и развивается как мультипарадигменный язык, впитывающий в себя различные методы и технологии программирования, но реализующий их на платформе, обеспечивающей высокую техническую эффективность. Поэтому в будущем не исключено добавление в язык средств функционального программирования, автоматической сборки мусора и других отсутствующих в нём сейчас механизмов. Но в любом случае это будет делаться на имеющейся платформе высокоэффективного компилируемого языка.
  • Хотя формально одним из принципов C++ остаётся сохранение совместимости с языком C, фактически группы по стандартизации этих языков не взаимодействуют, а вносимые ими изменения не только не коррелируют, но и нередко принципиально противоречат друг другу идеологически. Так, элементы, которые новые стандарты C добавляют в ядро, в стандарте C++ являются элементами стандартной библиотеки и в ядре вообще отсутствуют, например, динамические массивы, массивы с фиксированными границами, средства параллельной обработки. Как считает Страуструп, объединение разработки этих двух языков принесло бы большую пользу, но оно вряд ли возможно по политическим соображениям. Так что практическая совместимость между C и C++ постепенно будет утрачиваться.

Стандарт C++11: дополнения в ядре языка

Явно определяемые константные функции и выражения constexpr.

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

Универсальная инициализация.

Инициализация массивов и объектов списками константных значений, заключёнными в фигурные скобки. Значительно упростит инициализацию сложных объектов.

Конструкторы и операторы присваивания с семантикой переноса.

Отдельное описание для конструкторов и присваиваний, реализующих перенос значения из источника в приёмник (то есть не гарантирующих сохранность объекта-источника после завершения операции). Во многих случаях такие конструкторы и присваивания могут быть описаны значительно более эффективно, чем обычные копирующие конструкторы и полноценные присваивания. Наличие их позволяет компилятору использовать эти более эффективные описания при работе с временными объектами (представляющими результаты выражений, аргументы или возвращаемые значения функций) что, в итоге, увеличивает производительность программ, работающих со сложными объектами.

Конструкторы и операторы преобразования типов — новые возможности.

Разрешён прямой вызов одного конструктора из другого, что уменьшает дублирование кода и исключает необходимость создания скрытых методов-инициализаторов. Для операторов преобразования типов разрешено описание с ключевым словом explicit — запрет на неявное преобразование типа параметра при вызове.

Вывод типов.

Для применения в шаблонах, там, где затруднительно указать конкретный тип переменной, введены два новых механизма: переменные типа auto и описание decltype. Оба используются на месте типа переменной при её объявлении. Переменная типа auto должна быть явно инициализирована некоторым выражением и её тип компилятор определяет по типу значения выражения-инициализатора. Соответственно, если в инициализации участвуют значения шаблонных типов или вызовы шаблонных функций, тип переменной определяется с учётом конкретных подставленных типов-параметров этих шаблонов. Описание вида «decltype(x) y» понимается как «y имеет тип выражения x», то есть компилятор выводит тип выражения x и определяет y как переменную этого типа. Все эти операции производятся только на этапе компиляции, то есть статическая типизация не нарушается.

Чтобы обеспечить возможность указывать через decltype тип возвращаемого значения функции, когда он зависит от типов параметров, введён альтернативный синтаксис объявления функции: auto func(params) -> type соответствует type func(params). Например, auto f(T x, T y) -> decltype(x+y) определяет функцию f как возвращающую тип, выводимый из выражения «x+y»[пояснения 1]

Цикл по коллекции.

Вслед за многими современными языками в C++ введена конструкция «цикл по коллекции» вида for(type &x: array){...}. Здесь тело цикла выполняется для каждого элемента коллекции array, а x в каждой итерации будет ссылаться на очередной элемент коллекции. В качестве коллекции может выступать C-массив или любой контейнер стандартной библиотеки, для которого определены итераторы begin и end.

Лямбда-выражения.

Добавлена возможность объявлять лямбда-выражения (безымянные функции, определяемые в точке применения), в том числе зависящие от внешних переменных (замыкания). Лямбда-выражения могут присваиваться переменным и использоваться везде, где требуется функция соответствующего типа, например, в алгоритмах стандартной библиотеки.

Изменения в описании виртуальных методов.

Добавлен необязательный модификатор override, который употребляется в объявлении метода, замещающего виртуальный метод родительского класса. Описание замещения с override вызывает проверку на наличие в родительском классе замещаемого метода и на совпадение сигнатур методов.

Добавлен также модификатор final, как и в Java, запрещающий дальнейшее замещение помеченного им метода. Также final может быть объявлен класс — в таком случае от него запрещено наследовать новые классы.

Изменения в механизме шаблонов.

Добавлена возможность описания шаблонов с неопределённым числом параметров и возможность объявления синонимов для неспециализированных или частично специализированных шаблонов. Ключевое слово export при объявлении специализации шаблона указывает компилятору на то, что фактически специализация сделана в другой единице компиляции, что позволяет избежать повторной специализации одних и тех же шаблонов одними и теми же параметрами.

Различные синтаксические дополнения.

Определено ключевое слово для константы — нулевого указателя: nullptr. Его использование позволяет избежать путаницы между целым нулевым значением и пустым указателем, которая возникает при использовании константы 0, как требовалось ранее.

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

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

sizeof теперь может возвращать размер члена класса по его имени, без указания конкретного экземпляра (sizeof(ClassA::b)).

Стандарт C++11: изменения в стандартной библиотеке

  • Добавлена библиотека <regex>, реализующая общепринятые механизмы поиска и подстановки с помощью регулярных выражений.
  • Добавлена поддержка многопоточности.

Проблемные моменты

Одним из камней преткновения стало ключевое слово export в шаблонах, где оно используется для разделения объявления и определения спецификации шаблона (нововведение стандарта «C++98»). Герб Саттер, секретарь комитета по стандартизации C++, рекомендовал убрать export из будущих версий стандарта по причине серьёзных сложностей в полноценной реализации, однако впоследствии его решили оставить.

Первым компилятором, поддерживающим export в шаблонах, стал Comeau C++ в начале 2003 года (спустя 5 лет после выхода стандарта C++98). В 2004 году бета-версия компилятора Borland C++ Builder X также начала его поддержку. Оба этих компилятора основаны на фронт-энде EDG. Другие компиляторы, такие как Microsoft Visual C++ или GCC (до версии 3.4.4), вообще этого не поддерживают. Поддерживают export: Microsoft Visual C++ 7.0, GCC 3.4.4, Microsoft Visual Studio 2010 и другие.

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

Примеры программ

Пример создания простой программы на языке C++ в Ubuntu GNU/Linux

Пример № 1

Это пример программы, которая ничего не делает. Она начинает выполняться и немедленно завершается. Она состоит из основного потока: функции main(), которая обозначает точку начала выполнения программы на C++.

int main()

{

return 0;

}

Стандарт C++ требует, чтобы функция main() возвращала тип int. Программа, которая имеет другой тип возвращаемого значения функции main(), не соответствует стандарту C++.

Стандарт не говорит о том, что на самом деле означает возвращаемое значение функции main(). Традиционно оно интерпретируется как код возврата программы. Стандарт гарантирует, что возвращение 0 из функции main() показывает, что программа была завершена успешно.

Завершение программы на C++ с ошибкой традиционно обозначается путём возврата ненулевого значения.

Пример № 2

Эта программа также ничего не делает, но более лаконична.

int main(){}

В C++ (как и в C), если выполнение программы доходит до конца функции main(), то это эквивалентно return 0;. Это неверно для любой другой функции кроме main().

Пример № 3

Это пример программы Hello, world!, которая выводит сообщение, используя стандартную библиотеку, и завершается.

// это подключает заголовочный файл iostream

#include <iostream>

 

using namespace std;

 

int main()

{

cout << "Hello, world!" << endl; // можно и "...\n" вместо endl

return 0;

}

Пример № 4

Современный C++ позволяет решать простым способом и более сложные задачи. Этот пример демонстрирует кроме всего прочего использование контейнеров стандартной библиотеки шаблонов (STL).

#include <iostream> // для использования std::cout

#include <vector> // для std::vector<>

#include <map> // для std::map<> и std::pair<>

#include <algorithm> // для std::for_each()

#include <string> // для std::string

 

using namespace std; // используем пространство имён "std"

 

void display_item_count(pair < string const, vector<string> > const& person)

{

// person - это пара двух объектов: person.first - это его имя,

// person.second - это список его предметов (вектор строк)

cout << person.first << " is carrying " << person.second.size() << " items" << endl;

}

 

int main()

{

// Объявляем карту со строковыми ключами и данными в виде векторов строк

map< string, vector<string> > items;

 

// Добавим в эту карту пару человек и дадим им несколько предметов

items["Anya"].push_back("scarf");

items["Dmitry"].push_back("tickets");

items["Anya"].push_back("puppy");

 

// Переберём все объекты в контейнере

for_each(items.begin(), items.end(), display_item_count);

}

В этом примере для простоты используется директива использования пространства имён, в настоящей же программе обычно рекомендуется использовать объявления, которые аккуратнее директив:

#include <vector>

 

int main()

{

using std::vector;

 

vector<int> my_vector;

}

Здесь директива помещена в область функции, что уменьшает шансы столкновений имён (это и стало причиной введения в язык пространств имён). Использование объявлений, сливающих разные пространства имён в одно, разрушает саму концепцию пространства имён.[ источник не указан 1291 день ]

Пример № 5

Популярные библиотеки boost в сочетании со стандартными средствами языка позволяют очень лаконично и наглядно записывать код. В приведённом ниже примере вычисляется скалярное произведение векторов нечётных чисел и квадратов. В коде вектора значений представлены ленивыми STL-подобными последовательностями.

#include <iostream>

#include <numeric>

#include <boost/iterator/counting_iterator.hpp>

#include <boost/iterator/transform_iterator.hpp>

 

int odd(int i)

{

return 2 * i + 1;

}

 

int square(int i)

{

return i * i;

}

 

typedef boost::counting_iterator <int> counter;

typedef boost::transform_iterator <int (*)(int), counter> transformer;

 

transformer odds(int n)

{

return transformer(counter(n), odd);

}

 

transformer squares(int n)

{

return transformer(counter(n), square);

}

 

int main()

{

using namespace std;

 

cout << "Enter vector length: ";

int n; cin >> n;

 

cout << inner_product(odds(0), odds(n), squares(0), 0) << endl;

}

Данный пример демонстрирует так называемый «плоский» стиль записи. Это название связано с тем, что алгоритмы STL позволяют записывать код без циклов, соответственно ширина отступов в отформатированном коде примерно постоянна. Сторонники такого подхода считают, что программисту, знакомому со стандартной библиотекой С++, достаточно строчки с вызовом inner_product(), чтобы понять, что делает программа. С этой точки зрения вызов inner_product близок к словесному описанию задачи: «вычислить скалярное произведение векторов нечётных чисел и квадратов для значений от нуля до n».



Поделиться:


Последнее изменение этой страницы: 2016-12-12; просмотров: 156; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.221.146.223 (0.075 с.)