05-10-2023
Семантика: |
мультипарадигмальный: объектно-ориентированное, обобщённое, процедурное, метапрограммирование |
---|---|
Тип исполнения: | |
Появился в: | |
Автор(ы): | |
Расширение файлов: |
.c++ .cpp .cxx .cc .h++ .hpp .hxx .hh .h |
Типизация данных: | |
Основные реализации: |
GNU C++, Microsoft Visual C++, Intel C++ compiler, Clang, Comeau C/C++, Embarcadero (Borland) C++ Builder, Watcom C++ compiler, Digital Mars C++, Oracle Solaris Studio C++ compiler, Turbo C++ |
Диалекты: |
ISO/IEC 14882 C++ |
C++ — компилируемый статически типизированный язык программирования общего назначения.
Поддерживает такие парадигмы программирования как процедурное программирование, модульность, раздельная компиляция, обработка исключений, абстракция данных, типы (объекты), виртуальные функции, объектно-ориентированное программирование, обобщенное программирование, контейнеры и алгоритмы, сочетает свойства как высокоуровневых, так и низкоуровневых языков[1][2]. В сравнении с его предшественником — языком C, — наибольшее внимание уделено поддержке объектно-ориентированного и обобщённого программирования.[2] Название «C++» происходит от названия языка C, в котором унарный оператор ++
обозначает инкремент переменной.
Являясь одним из самых популярных языков программирования,[3][4] C++ широко используется для разработки программного обеспечения. Область его применения включает создание операционных систем, разнообразных прикладных программ, драйверов устройств, приложений для встраиваемых систем, высокопроизводительных серверов, а также развлекательных приложений (например, видеоигры). Существует несколько реализаций языка C++ — как бесплатных, так и коммерческих. Наиболее популярны проект GNU, Microsoft, Intel и Embarcadero (Borland). C++ оказал огромное влияние на другие языки программирования, в первую очередь на Java и C#.
При создании C++ Бьёрн Страуструп стремился сохранить совместимость с языком C. Множество программ, которые могут одинаково успешно транслироваться как компиляторами C, так и компиляторами C++, довольно велико — отчасти благодаря тому, что синтаксис C++ был основан на синтаксисе C.
Исторический этап развития[5] | Год |
---|---|
Язык BCPL | 1966 |
Язык Би (оригинальная разработка Томпсона под UNIX) | 1969 |
Язык Си (добавление Ритчи типа и управляющих структур в язык Би) | 1973 |
Си с классами | 1979 |
C84 | 1984 |
Cfront (выпуск E) | 1984 |
Cfront (выпуск 1.0) | 1985 |
Появление множественного/виртуального наследования | 1988 |
Обобщенное программирование (шаблоны) | 1991 |
ANSI C++ / ISO-C++ | 1996 |
ISO/IEC 14882:1998 | 1998 |
ISO/IEC 14882:2003 | 2003 |
C++/CLI | 2005 |
TR1 | 2005 |
C++11 | 2011 |
Язык возник в начале 1980-х годов, когда сотрудник фирмы Bell Laboratories Бьёрн Страуструп придумал ряд усовершенствований к языку C под собственные нужды. До начала официальной стандартизации язык развивался в основном силами Страуструпа в ответ на запросы программистского сообщества. В 1998 году был ратифицирован международный стандарт языка C++: ISO/IEC 14882:1998 «Standard for the C++ Programming Language»; после принятия технических исправлений к стандарту в 2003 году — нынешняя версия этого стандарта — ISO/IEC 14882:2003.[6]
Ранние версии языка, известные под именем «C с классами», начали появляться с 1980 года.[7] Идея создания нового языка берёт начало от опыта программирования Страуструпа для диссертации. Он обнаружил, что язык моделирования Simula имеет такие возможности, которые были бы очень полезны для разработки большого программного обеспечения, но работает слишком медленно. В то же время язык BCPL достаточно быстр, но слишком близок к языкам низкого уровня и не подходит для разработки большого программного обеспечения. Страуструп начал работать в Bell Labs над задачами теории очередей (в приложении к моделированию телефонных вызовов). Попытки применения существующих в то время языков моделирования оказались неэффективными. Вспоминая опыт своей диссертации, Страуструп решил дополнить язык C (преемник BCPL) возможностями, имеющимися в языке Симула. Язык C, будучи базовым языком системы UNIX, на которой работали компьютеры Bell, является быстрым, многофункциональным и переносимым. Страуструп добавил к нему возможность работы с классами и объектами. В результате, практические задачи моделирования оказались доступными для решения как с точки зрения времени разработки (благодаря использованию Симула-подобных классов) так и с точки зрения времени вычислений (благодаря быстродействию C). Вначале в C были добавлены классы (с инкапсуляцией), производные классы, строгая проверка типов, inline-функции и аргументы по умолчанию.
Разрабатывая C с классами (позднее C++), Страуструп также написал программу cfront — транслятор, перерабатывающий исходный код C с классами в исходный код простого C. Новый язык, неожиданно для автора, приобрёл большую популярность среди коллег и вскоре Страуструп уже не мог лично поддерживать его, отвечая на тысячи вопросов.
В 1983 году произошло переименование языка из C с классами в C++. Кроме того, в него были добавлены новые возможности, такие как виртуальные функции, перегрузка функций и операторов, ссылки, константы, пользовательский контроль над управлением свободной памятью, улучшенная проверка типов и новый стиль комментариев (//
). Его первый коммерческий выпуск состоялся в октябре 1985 года. В 1985 году вышло также первое издание «Языка программирования C++», обеспечивающее первое описание этого языка, что было чрезвычайно важно из-за отсутствия официального стандарта. В 1989 году состоялся выход C++ версии 2.0. Его новые возможности включали множественное наследование, абстрактные классы, статические функции-члены, функции-константы и защищённые члены.
В 1990 году вышло «Комментированное справочное руководство по C++», положенное впоследствии в основу стандарта. Последние обновления включали шаблоны, исключения, пространства имён, новые способы приведения типов и булевский тип.
Стандартная библиотека C++ также развивалась вместе с ним. Первым добавлением к стандартной библиотеке C++ стали потоки ввода/вывода, обеспечивающие средства для замены традиционных функций C printf
и scanf
. Позднее самым значительным развитием стандартной библиотеки стало включение в неё Стандартной библиотеки шаблонов.
Никто не обладает правами на язык C++, он является свободным. Однако сам документ стандарта языка (за исключением черновиков) не доступен бесплатно.
В 1998 году был опубликован стандарт языка ISO/IEC 14882:1998 (известный как C++98),[8] разработанный комитетом по стандартизации C++ (ISO/IEC JTC1/SC22/WG21 working group).
В 2003 году был опубликован стандарт языка ISO/IEC 14882:2003, где были исправлены выявленные ошибки и недочёты предыдущей версии стандарта.
В 2005 году был выпущен отчёт «Library Technical Report 1» (кратко называемый TR1). Не являясь официально частью стандарта, отчёт описывает расширения стандартной библиотеки, которые, как ожидалось авторами, должны быть включены в следующую версию языка C++. Степень поддержки TR1 улучшается почти во всех поддерживаемых компиляторах языка C++.
С 2009 года велась работа по обновлению предыдущего стандарта, предварительной версией нового стандарта сперва был C++99, а спустя год C++0x, сегодня — C++11, куда были включены дополнения в ядро языка и расширение стандартной библиотеки, в том числе большую часть TR1.
Имя языка, получившееся в итоге, происходит от оператора унарного постфиксного инкремента C ++
(увеличение значения переменной на единицу). Имя C+ не было использовано потому, что является синтаксической ошибкой в C и, кроме того, это имя было занято другим языком. Язык также не был назван B, поскольку «является расширением C и не пытается устранять проблемы путём удаления элементов C».[7]
В книге «Дизайн и эволюция C++» Бьёрн Страуструп описывает принципы, которых он придерживался при проектировании C++.[9] Эти принципы объясняют, почему C++ именно такой, какой он есть. Некоторые из них:
Стандарт C++ на 2003 год состоит из двух основных частей: описание ядра языка и описание стандартной библиотеки.
Кроме того, существует огромное количество библиотек C++, не входящих в стандарт. В программах на C++ можно использовать многие библиотеки C.
Стандартизация определила язык программирования C++, однако за этим названием могут скрываться также неполные, ограниченные, достандартные варианты языка. Первое время язык развивался вне формальных рамок, спонтанно, по мере встававших перед ним задач. Развитию языка сопутствовало развитие кросс-компилятора cfront. Новшества в языке отражались в изменении номера версии кросс-компилятора. Эти номера версий кросс-компилятора распространялись и на сам язык, но применительно к настоящему времени речь о версиях языка C++ не ведут.
В этом разделе описываются возможности, непосредственно не связанные с объектно-ориентированным программированием (ООП), но многие из них, однако, особенно важны в сочетании с ООП.
С++ поддерживает как комментарии в стиле C
/*
это комментарий, который может состоять
из нескольких строчек
*/
, так и однострочные
// вся оставшаяся часть строки является комментарием
, где //
обозначает начало комментария, а ближайший последующий символ новой строки, который не предварён символом \
(либо эквивалентным ему обозначением ??/
), считается окончанием комментария.
В C++ доступны следующие встроенные типы:
char
, wchar_t
(char16_t
и char32_t
, в стандарте C++11).signed char
, short int
, int
, long int
(и long long int
, в стандарте C++11).unsigned char
, unsigned short int
, unsigned int
, unsigned long int
(и unsigned long long int
, в стандарте C++11).float
, double
, long double
.bool
, имеющий значения true
и false
.Операции сравнения возвращают тип bool
. Выражения в скобках после if
, while
приводятся к типу bool
.[10]
Функции могут принимать аргументы по ссылке. Например, функция void f(int &x) {x=3;}
присваивает своему аргументу значение 3. Функции также могут возвращать результат по ссылке, и ссылки могут быть вне всякой связи с функциями. Например, {double &b=a[3]; b=sin(b);}
эквивалентно a[3]=sin(a[3]);
. При программировании ссылки в определённой степени сходны с указателями, со следующими особенностями: перед использованием ссылка должна быть инициализирована; ссылка пожизненно указывает на один и тот же адрес; в выражении ссылка обозначает непосредственно тот объект или ту функцию, на которую она указывает, обращение же к объекту или функции через указатель требует разыменование указателя. Существуют и другие отличия в использовании указателей и ссылок. Концептуально ссылка — другое имя переменной или функции, другое название одного и того же адреса, существует лишь только в тексте программы, заменяемое адресом при компиляции; а указатель — переменная, хранящая адрес, к которому обращаются.
inline
позволяет объявлять inline-функции. Функция, определённая внутри тела класса, является inline по умолчанию. Изначально inline-функции задумывались как функции, являющиеся хорошими кандидатами на оптимизацию, при которой в местах обращения к функции компилятор вставит тело этой функции, а не код вызова. В действительности компилятор не обязан реализовывать подстановку тела для inline-функций, но может, исходя из заданных критериев оптимизации, выполнять подстановку тела для функций, которые не объявлены как inline
. Пожалуй, наиболее значимой особенностью inline-функции является то, что она может многократно определяться в нескольких единицах трансляции (при этом inline-функция должна быть определена во всех единицах трансляции, где она используется), в то время как функция, не являющаяся inline, может определяться в программе не более одного раза. Пример:inline double Sqr(double x) {return x*x;}
.
volatile
используется в описании переменных и информирует компилятор, что значение данной переменной может быть изменено способом, который компилятор не в состоянии отследить. Для переменных, объявленных volatile
, компилятор не должен применять средства оптимизации, изменяющие положение переменной в памяти (например, помещающие её в регистр) или полагающиеся на неизменность значения переменной в промежутке между двумя присваиваниями ей значения.union
) или перечисление (enum
), её имя является именем типа, например:struct Time { int hh, mm, ss; }; Time t1, t2;
namespace Foo { const int x=5; typedef int** T; void f(int y) {return y*x}; double g(T); ... }
то вне фигурных скобок следует обращаться к T, x, f, g как Foo::T, Foo::x, Foo::f и Foo::g соответственно. Если в каком-то файле нужно обратиться к ним непосредственно, можно написать
using namespace Foo;
Или же
using Foo::T;
Пространства имён нужны, чтобы не возникало коллизий между пакетами, имеющими совпадающие имена глобальных переменных, функций и типов. Специальным случаем является безымянное пространство имён
namespace { ... }
Все имена, описанные в нём, доступны в текущей единице трансляции и больше нигде.
void f(int x, int y=5, int z=10)
, вызовы f(1)
, f(1,5)
и f(1,5,10)
эквивалентны.int printf(const char* fmt, ...)
.typedef
, так и через описание других классов, а также перечислений. Для доступа к таким типам вне класса, к имени типа добавляется имя структуры или класса и два двоеточия:struct S { typedef int** T; T x; }; S::T y;
void Print(int x); void Print(double x); void Print(int x, int y);
struct Date {int day, month, year;}; void operator ++(struct Date& date);
Операторные функции во многом схожи с обычными (неоператорными) функциями. За исключением операторов new
, new[]
, delete
и delete[]
, нельзя переопределять поведение операторов для встроенных типов (скажем, переопределять умножение значений типа int); нельзя выдумывать новые операторы, которых нет в C++ (скажем, **
); нельзя менять количество операндов, предусмотренное для оператора, а также нельзя менять существующие приоритеты и ассоциативность операторов (скажем, в выражении a+b*c
сначала будет выполняться умножение, а потом сложение, к каким бы типам ни принадлежали a, b и c). Можно переопределить операции []
(с одним параметром) и ()
(с любым числом параметров).
template
). Например, template<class T> T Min(T x, T y) {return x<y?x:y;}
определяет функцию Min для любых типов. Шаблоны могут задавать не только функции, но и типы. Например, template<class T> struct Array{int len; T* val;};
определяет массив значений любого типа, после чего мы можем писать Array<float> x;
malloc
и free
введены операторные функции operator new
, operator new[]
, operator delete
и operator delete[]
, а также операторы new
, new[]
, delete
и delete[]
. Если T — произвольный объектный тип, не являющийся типом массива, X — произвольный объектный тип и A — тип массива из некоторого количества n элементов, имеющих тип X, то
new T
выделяет память (посредством вызова функции operator new
), достаточную для размещения одного объекта типа Т, возможно, инициализирует объект в этой памяти, и возвращает указатель типа Т*
(например, Т* p = new T
).new X[n]
и new A
выделяют память (посредством вызова функции operator new[]
), достаточную для размещения n объектов типа X, возможно, инициализируют каждый объект в этой памяти, и возвращают указатель типа X*
(например, X* p = new X[n]
).delete p
— разрушает объект (не являющийся массивом), на который ссылается указатель p, и освобождает область памяти (посредством вызова функции operator delete
), ранее выделенную для него new-выражением.delete [] p
— разрушает каждый объект в массиве, на который ссылается указатель p, и освобождает область памяти (посредством вызова функции operator delete[]
), ранее выделенную для этого массива new-выражением.Операция delete
проверяет, что её аргумент не NULL, в противном случае она ничего не делает. Для инициализации объекта non-POD классового типа new-выражение вызывает конструктор; для уничтожения объекта классового типа delete-выражение вызывает деструктор (см. ниже).
C++ добавляет к C объектно-ориентированные возможности. Он вводит классы, которые обеспечивают три самых важных свойства ООП: инкапсуляцию, наследование и полиморфизм.
В стандарте C++ под классом (class) подразумевается пользовательский тип, объявленный с использованием одного из ключевых слов class
, struct
или union
, под структурой (structure) подразумевается класс, определённый через ключевое слово struct, и под объединением (union) подразумевается класс, определённый через ключевое слово union.
В теле класса можно указать только заголовок функции, а можно описать всю функцию (см. пример с функцией Alloc
ниже. В этом случае она считается встраиваемой (inline
))
Нестатические функции-члены (и только они) могут иметь описатель const
class Array { ... inline double operator[] (int n) const;
Такие функции не имеют права изменять поля класса (кроме полей, определённых как mutable
). Если они пытаются это сделать, компилятор должен выдать сообщение об ошибке.
В C++ при наследовании одного класса от другого наследуется реализация класса, плюс класс-наследник может добавлять свои поля и функции или переопределять функции базового класса. Множественное наследование разрешено.
Конструктор наследника вызывает конструкторы базовых классов, а затем конструкторы нестатических членов-данных, являющихся экземплярами классов. Деструктор работает в обратном порядке.
Наследование бывает публичным, защищённым и закрытым (то есть закрытого типа):
Доступ члена базового класса/режим наследования | private-член | protected-член | public-член |
---|---|---|---|
private-наследование | недоступен | private | private |
protected-наследование | недоступен | protected | protected |
public-наследование | недоступен | protected | public |
Наследник — это больше чем базовый класс, поэтому, если наследование открытое, то он может использоваться везде, где используется базовый класс, но не наоборот.
Полиморфизмом в программировании называется переопределение наследником функций-членов базового класса, например:
class Figure { ... void Draw() const; ... }; class Square : public Figure { ... void Draw() const; ... }; class Circle : public Figure { ... void Draw() const; ... };
Какая именно из функций будет вызвана — Figure::Draw()
, Square::Draw()
или Circle::Draw()
— определяется во время компиляции. Например:
Circle *c = new Circle(0,0,5); Figure *f = c; // Всё ok: Figure — базовый класс для Circle c->Draw(); f->Draw(); // Указатели друг другу равны, но для f будет вызвана другая функция, чем для c
Несмотря на то что оба указателя указывают на один и тот же объект класса Circle, для c будет вызвана Circle::Draw()
, а для f — Figure::Draw()
, поскольку f — указатель на объект класса Figure. Такой полиморфизм называется статическим.
Но в C++ есть и динамический полиморфизм, когда вызываемая функция определяется во время выполнения. Для этого функции-члены базового класса должны быть объявлены виртуальными.
class Figure { ... virtual void Draw() const; ... }; class Square : public Figure { ... void Draw() const; ... }; class Circle : public Figure { ... void Draw() const; ... }; Figure * figures[10]; figures[0] = new Square(1, 2, 10); figures[1] = new Circle(3, 5, 8); ... for (int i = 0; i < 10; i++) figures[i]->Draw();
В этом случае для каждого элемента массива будет вызвана Square::Draw()
или Circle::Draw()
в зависимости от вида фигуры.
Чисто виртуальной функцией называется виртуальная функция-член, которая объявлена со спецификатором = 0
:
class Figure { ... virtual void Draw() const = 0; );
Чисто виртуальная функция может быть оставлена без определения, кроме случая, когда требуется произвести её вызов.
Абстрактным классом называется такой, у которого есть хотя бы одна чисто виртуальная функция-член. Объекты таких классов создавать запрещено. Абстрактные классы часто используются как интерфейсы. В отличие от чистых интерфейсов других языков, абстрактные классы С++ могут иметь невиртуальные функции и члены-данные.
Основным способом организации информации в C++ являются классы. В отличие от структуры (struct
) языка C, которая может состоять только из полей и вложенных типов, класс (class
) C++ может состоять из полей, вложенных типов и функций-членов (member functions). Инкапсуляция в С++ реализуется через указание уровня доступа к членам класса: они бывают публичными (открытыми, public
), защищёнными (protected
) и собственными (закрытыми, приватными, private
). В C++ структуры формально отличаются от классов лишь тем, что по умолчанию члены и базовые классы у структуры публичные, а у класса — собственные.
Доступ | private | protected | public |
---|---|---|---|
Сам класс | да | да | да |
Друзья | да | да | да |
Наследники | нет | да | да |
Извне | нет | нет | да |
Проверка доступа происходит во время компиляции, попытка обращения к недоступному члену класса вызовет ошибку компиляции.
Пример класса, реализующего одномерный массив (это просто иллюстрация, а не образец дизайна!):
class Array { public: Array() : len(0), val(NULL) {} Array(int _len) : len(_len) { val = new double[_len]; } Array(const Array & a); ~Array() { Free(); } inline const double & Elem(int i) const { return val[i]; } inline void ChangeElem(int i, double x) { val[i] = x; } protected: void Alloc(int _len) { if (len == 0) Free(); len = _len; val = new double[len]; } void Free() { delete [] val; len = 0; } int len; double * val; };
Здесь класс Array имеет 2 публичных функции-члена, 2 защищённых поля, 3 публичных конструктора и публичный деструктор. Описатель inline
означает подсказку компилятору, что вместо вызова функции её код следует встроить в точку вызова, чем часто можно достичь большей эффективности.
Функции-друзья — это функции, не являющиеся функциями-членами и тем не менее имеющие доступ к защищённым и закрытым членам класса. Они должны быть объявлены в теле класса как friend
. Например:
class Matrix { ... friend Matrix Multiply(Matrix m1, Matrix m2); ... }; Matrix Multiply(Matrix m1, Matrix m2) { ... }
Здесь функция Multiply может обращаться к любым полям и функциям-членам класса Matrix.
Существуют также классы-друзья. Если класс A — друг класса B, то все его собственные (не унаследованные) функции-члены могут обращаться к любым членам класса B. Например:
class Matrix { ... friend class Vector; ... };
Однако в C++ не действует правило «друг моего друга — мой друг».
По действующему стандарту C++ вложенный класс не имеет прав доступа к закрытым членам объемлющего класса и не может быть объявлен его другом (последнее следует из определения термина друг как нечлена класса). В будущем стандарте C++0x эти ограничения будут устранены. В данном отношении современные версии компиляторов VC++, GNU C++ и Comeau C++ даже с отключенными расширениями следуют новым правилам, сформулированным в последних версиях черновика C++0x.
В классах всегда есть специальные функции — конструкторы и деструкторы, которые могут быть объявлены явно или неявно.
Конструктор вызывается для инициализации объекта (соответствующего типа) при его создании, а деструктор — для уничтожения объекта. В частности, конструктор может быть вызван для выполнения преобразования к классовому типу.
Конструкторы обозначаются как одноимённые классу функции (например, Array::Array
), деструкторы — как имя класса, предварённое тильдой (например, Array::~Array
). Для конструкторов и деструкторов нельзя указывать тип возвращаемого значения. Деструктор нельзя объявлять как принимающий аргументы. Класс может иметь сколько угодно конструкторов (с разными наборами параметров), в том числе шаблонных, и только один (причём нешаблонный) деструктор.
Конструктор без параметров или конструктор, все параметры которого имеют аргументы по умолчанию, называется конструктором по умолчанию, нешаблонный конструктор с первым параметром-ссылкой на тот же класс (например, Array::Array(const Array&)
) и остальными параметрами (если таковые есть), имеющими аргументы по умолчанию, — конструктором копирования, он вызывается при создании нового объекта, являющегося копией уже существующего объекта:
Array a(5); // вызывается Array::Array(int) Array b; // вызывается Array::Array() Array c(a); // вызывается Array::Array(const Array&) Array d=a; // вызывается Array::Array(const Array&) b=c; // происходит вызов оператора = // если он не определён (как в данном случае), то вызывается сгенерированный компилятором оператор присваивания, который // осуществляет копирование базовых подобъектов и почленное копирование нестатических членов-данных. // как правило конструктор копий и оператор присваивания переопределяются попарно
Если в классе нет явно объявленных конструкторов, то класс имеет неявно объявленный конструктор без параметров. Если в классе нет явно объявленных копирующих конструкторов, то класс имеет неявно объявленный копирующий конструктор. Если в классе нет явно объявленного деструктора, то класс имеет неявно объявленный деструктор.
Функции-члены могут быть операторами:
class Array { ... inline double& operator[] (int n) { return val[n]; }
И далее
Array a(10); ... double b = a[5];
Стандартная библиотека С++ включает в себя как сущности, специфичные для С++, так и, частично, стандартную библиотеку C с небольшими изменениями1990 года и не определяет самостоятельно те функции стандартной библиотеки, которые заимствуются из стандартной библиотеки C. Доступ к возможностям стандартной библиотеки C++ обеспечивается с помощью включения в программу (посредством директивы #include
) соответствующих стандартных заголовочных файлов. Всего в стандарте C++ определено 50 таких файлов (например, <cstddef>
— описания стандартных типов, <new>
— управление динамическим выделением памяти и т. д.).
Для использования следующих функций стандартной библиотеки
void* operator new(std::size_t) throw(std::bad_alloc); void* operator new[](std::size_t) throw(std::bad_alloc); void operator delete(void*) throw(); void operator delete[](void*) throw();
подключение каких-либо заголовочных файлов не требуется.
STL до включения в стандарт C++ была сторонней разработкой, в начале — фирмы HP, а затем SGI. Стандарт языка не называет её «STL», так как эта библиотека стала неотъемлемой частью языка, однако многие люди до сих пор используют это название, чтобы отличать её от остальной части стандартной библиотеки (потоки ввода/вывода (iostream), подраздел C и другие).
Проект под названием STLport[11], основанный на SGI STL, осуществляет постоянное обновление STL, IOstream и строковых классов. Некоторые другие проекты также занимаются разработкой частных применений стандартной библиотеки.
Нововведениями C++ в сравнении с C являются:
Новые возможности C++ включают объявления в виде выражений, преобразования типов в виде функций, операторы new
и delete
, тип bool
, ссылки, расширенное понятие константности, подставляемые функции, аргументы по умолчанию, переопределения, пространства имён, классы (включая и все связанные с классами возможности, такие как наследование, функции-члены, виртуальные функции, абстрактные классы и конструкторы), переопределения операторов, шаблоны, оператор ::
, обработку исключений, динамическую идентификацию и многое другое. Язык C++ также во многих случаях строже относится к проверке типов, чем C.
В C++ появились комментарии в виде двойной косой черты (//
), которые были в предшественнике C — языке BCPL.
Некоторые особенности C++ позднее были перенесены в C, например, ключевые слова const
и inline
, объявления в циклах for
и комментарии в стиле C++ (//
). В более поздних реализациях C также были представлены возможности, которых нет в C++, например макросы va_arg
и улучшенная работа с массивами-параметрами.
Несмотря на то, что большая часть кода C будет справедлива и для C++, C++ не является надмножеством C и не включает его в себя. Существует и такой верный для C код, который неверен для C++. Это отличает его от Objective C, ещё одного усовершенствования C для ООП, как раз являющегося надмножеством C.
Существуют и другие различия. Например, C++ не разрешает вызывать функцию main()
внутри программы, в то время как в C это действие правомерно. Кроме того, C++ более строг в некоторых вопросах; например, он не допускает неявное приведение типов между несвязанными типами указателей и не разрешает использовать функции, которые ещё не объявлены.
Более того, код, верный для обоих языков, может давать разные результаты в зависимости от того, компилятором какого языка он оттранслирован. Например, на большинстве платформ следующая программа печатает «С», если компилируется компилятором C, и «C++» — если компилятором C++. Так происходит из-за того, что символьные константы в C (например, 'a'
) имеют тип int
, а в C++ — тип char
, а размеры этих типов обычно различаются.
#include <stdio.h> int main() { printf("%s\n", (sizeof('a') == sizeof(char)) ? "C++" : "C"); return 0; }
Текущий стандарт языка ISO/IEC 14882:2003(E) был принят в 2003 году. Неофициально его обозначают как C++03. Следующая версия стандарта имеет неофициальное обозначение C++11.
C++ продолжает развиваться, чтобы отвечать современным требованиям. Одна из групп, занимающихся языком C++ в его современном виде и направляющих комитету по стандартизации C++ советы по его улучшению — это Boost. Например, одно из направлений деятельности этой группы — совершенствование возможностей языка путём добавления в него особенностей метапрограммирования.
Стандарт C++ не описывает способы именования объектов, некоторые детали обработки исключений и другие возможности, связанные с деталями реализации, что делает несовместимым объектный код, созданный различными компиляторами. Однако для этого третьими лицами создано множество стандартов для конкретных архитектур и операционных систем.
Одной из точек преткновения в этом вопросе является ключевое слово export
, используемое также и для разделения объявления и определения шаблонов.
Первым компилятором, поддерживающим export
в шаблонах, стал Comeau C++ в начале 2003 года (спустя 5 лет после выхода стандарта C++98). В 2004 году бета-версия компилятора Borland C++ Builder X также начала его поддержку.
Оба этих компилятора основаны на фронт-энде EDG. Другие компиляторы, такие как Microsoft Visual C++ или GCC (GCC 3.4.4), вообще этого не поддерживают. Герб Саттер, секретарь комитета по стандартизации C++, рекомендовал убрать export
из будущих версий стандарта по причине серьёзных сложностей в полноценной реализации, однако впоследствии его решили оставить. Поддерживают export
: Microsoft Visual C++ 7.0, GCC 3.4.4, Microsoft Visual Studio 2010 и другие.
Из списка других проблем, связанных с шаблонами, можно привести вопросы конструкций частичной специализации шаблонов, которые плохо поддерживались в течение многих лет после выхода стандарта C++.
Это пример программы, которая ничего не делает. Она начинает выполняться и немедленно завершается. Она состоит из основного потока: функции main()
, которая обозначает точку начала выполнения программы на C++.
int main() { return 0; }
Стандарт C++ требует, чтобы функция main()
возвращала тип int
. Программа, которая имеет другой тип возвращаемого значения функции main()
, не соответствует стандарту C++.
Стандарт не говорит о том, что на самом деле означает возвращаемое значение функции main()
. Традиционно оно интерпретируется как код возврата программы. Стандарт гарантирует, что возвращение 0 из функции main()
показывает, что программа была завершена успешно.
Завершение программы на C++ с ошибкой традиционно обозначается путём возврата ненулевого значения.
Эта программа также ничего не делает, но более лаконична.
int main(){}
В C++ (как и в C), если выполнение программы доходит до конца функции main()
, то это эквивалентно return 0;
. Это неверно для любой другой функции кроме main()
.
Это пример программы Hello World, которая выводит сообщение, используя стандартную библиотеку, и завершается.
#include <iostream> // это необходимо для std::cout и std::endl и для перегруженного оператора << int main() { std::cout << "Hello, world!" << std::endl; return 0; }
Современный 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; }
Здесь директива помещена в область функции, что уменьшает шансы столкновений имён (это и стало причиной введения в язык пространств имён). Использование объявлений, сливающих разные пространства имён в одно, разрушает саму концепцию пространства имён.
Популярные библиотеки 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».
Прежде всего, необходимо подчеркнуть, что оценивать достоинства и, в особенности, недостатки C++ необходимо в контексте тех принципов, на которых строился язык, и требований, которые к нему изначально предъявлялись.
C++ — чрезвычайно мощный язык, содержащий средства создания эффективных программ практически любого назначения, от низкоуровневых утилит и драйверов до сложных программных комплексов самого различного назначения. В частности:
std::numeric_limits <T>
). Доступны компиляторы для большого количества платформ, на языке C++ разрабатывают программы для самых различных платформ и систем.Критику языка C++ можно разделить на несколько групп заявлений. Критики полагают, что:
=
, а операция сравнения как ==
. Их легко спутать, при этом операция присваивания возвращает значение, поэтому присваивание на месте выражения является синтаксически корректным, а в конструкциях цикла и ветвления появление числа на месте логического значения также допустимо, так что ошибочная конструкция оказывается синтаксически правильной. Типичный пример подобной ошибки:
if (x=0) { операторы }
if
. Так как нуль соответствует логическому значению «ложь», блок операторов в условной конструкции не выполнится никогда. Ошибки такого рода трудно выявлять, но во многих современных компиляторах предлагается диагностика некоторых подобных конструкций.=
), инкрементации (++
), декрементации (--
) и другие возвращают значение. В сочетании с обилием операций это позволяет, хотя и не обязывает, создавать трудночитаемые выражения. Наличие этих операций в C было вызвано желанием получить инструмент ручной оптимизации кода , но в настоящее время оптимизирующие компиляторы обычно генерируют оптимальный код и на традиционных выражениях. С другой стороны, один из основных принципов языков C и C++ — позволять программисту писать в любом стиле, а не навязывать «хороший» стиль.dynamic_cast
позволяет привести указатель или ссылку строго в пределах иерархии классов. Это делает код более надёжным, декларативным и позволяет находить приведения в пределах иерархии при помощи инструментов типа grep. Однако вследствие требования высокой степени совместимости с C старое приведение типов всё ещё поддерживается.break
в ветви оператора switch
с целью последовательного выполнения нескольких ветвей. Такой же подход принят в языке Java[12]. Есть мнение, что это затрудняет понимание кода. Например, в языке C# необходимо всегда писать либо break
, либо использовать goto case N
для явного указания порядка выполнения[13].#define
) являются мощным, но опасным средством. Они сохранены в C++ несмотря на то, что необходимость в них, благодаря шаблонам и встроенным функциям, не так уж велика. В унаследованных стандартных C-библиотеках много потенциально опасных макросов[14].#include
) серьёзно замедляет компиляцию при подключении большого количества модулей (потому что результирующий файл, который обрабатывается компилятором, оказывается очень велик). Эта схема без изменений скопирована в C++. Для устранения этого недостатка многие компиляторы реализуют механизм прекомпиляции заголовочных файлов.Сторонники языка считают, что при оценке критических высказываний по поводу С++ необходимо учитывать следующее:
См. также: (англ.) Comparison of Java and C++
Целью создания C++ было расширение возможностей C, наиболее распространённого языка системного программирования. Ориентированный на ту же самую область применения, C++ унаследовал множество не самых лучших, с теоретической точки зрения, особенностей C[каких?]. Перечисленные выше принципы, которых придерживался автор языка, предопределили многие недостатки C++ .
В области прикладного программирования альтернативой C++ стал его язык-потомок, Java. Несмотря на преемственность по отношению к C++, Java строилась на принципиально иной основе, её разработчики не были связаны требованиями совместимости с языком-предком и обеспечения максимально достижимой эффективности, благодаря чему они смогли кардинально переработать язык, отказаться от множества синтаксических средств, чтобы добиться идеологической целостности языка. Позже компания Microsoft предложила язык C#, представляющий собой ещё одну переработку C++ в том же направлении, что и Java. В дальнейшем появился язык Nemerle, в котором к средствам C# добавлены средства функционального программирования. Ещё позже появилась попытка объединения эффективности C++ с безопасностью и скоростью разработки Java и C# — был предложен язык D, который пока не получил широкого признания.
Java и C++ можно рассматривать как два языка-потомка C, разработанных из различных соображений и пошедших, вследствие этого, по разным путям. В связи с этим представляет интерес сравнение данных языков (всё, сказанное ниже про Java, можно с равным успехом отнести к языкам C# и Nemerle, поскольку в рассматриваемых деталях эти языки отличаются лишь внешне).
std::vector
и std::ifstream
). Также возможен подход, когда программист, выделяя ресурсы (память под объекты, открытые файлы и т. п.), обязан явно позаботиться о своевременном их освобождении. Java работает в среде со сборкой мусора, которая автоматически отслеживает прекращение использования объектов и освобождает занимаемую ими память, если в этом есть необходимость, в некоторый неопределённый момент времени. Ручное управление предпочтительнее в системном программировании, где требуется полный контроль над ресурсами, RAII и сборка мусора удобнее в прикладном программировании, поскольку в значительной степени освобождают программиста от необходимости отслеживать момент прекращения использования ресурсов. Сборщик мусора Java требует системных ресурсов, что снижает эффективность выполнения программ, лишает программы на Java детерминированности выполнения и способен следить только за памятью. Файлы, каналы, сокеты, объекты графического интерфейса программист на Java всегда освобождает явно.Отличия языков приводят к ожесточённым спорам между сторонниками двух языков о том, какой язык лучше. Споры эти во многом беспредметны, поскольку сторонники Java считают различия говорящими в пользу Java, а сторонники C++ полагают обратное. C++, в свою очередь, развивался, и ряд его недостатков устранён в последних версиях стандарта (например, появился механизм частичной спецификации шаблонов).
Далеко не все программисты являются сторонниками одного из языков
. По мнению большинства программистов, Java и C++ не являются конкурентами, потому что обладают различными областями применимости. Другие считают , что выбор языка для многих задач является вопросом личного вкуса.Программирование на языке C++ в Викиверситете? |
switch
statement is the value of the declared variable implicitly converted to bool
… The value of a condition that is an expression is the value of the expression, implicitly converted to bool
for statements other than switch
; if that conversion is ill-formed, the program is ill-formed».Основные языки программирования (сравнение • IDE • история • хронология) | |
---|---|
Используемые в разработке |
Ада • APL • Язык ассемблера • ActionScript • ABAP/4 • AutoIt • AWK • Бейсик • Си • Кобол • C++ • C# • Cω • Clarion • Clojure • ColdFusion • Common Lisp • D • dBase • Delphi • Eiffel • Erlang • Euphoria • F# • Форт • Фортран • Gambas • Go • Groovy • HAL/S • Haskell • Icon • Java • JavaScript • Limbo • Lua • Модула-3 • Object Pascal • Objective-C • OCaml • Oz • Parser • Паскаль • Компонентный Паскаль • Perl • PHP • PowerBASIC • Python • ПЛ/1 • Пролог • Ruby • Scala • Scheme • Smalltalk • SQL • PL/SQL • Tcl • Vala • Visual Basic (.NET) |
Академические | |
IEC 61131-3 |
Instruction List • ST • FBD • Ladder Diagram (LD) • SFC |
Прочие | |
Эзотерические | |
Визуальные |
Язык программирования Си | |
---|---|
Компиляторы | Borland Turbo C • Clang • GCC • LCC • Pelles C • PCC • TCC • Visual C++ (C++/CLI • C++/CX) • Watcom C/C++ compiler |
Библиотеки | Стандартная библиотека языка Си • glibc • dietlibc • uClibc • Newlib • Eglibc • Bionic |
Особенности | C-строка • Синтаксис • Препроцессор • Типы и объявления переменных • Функции |
Избранные потомки | C++ • C# • D • Objective-C • Alef • Limbo • Go • Vala |
C и другие языки | C и C++ (Совместимость • Операторы) • Сравнение Pascal и C • Компилятор C в байт-код Java |
Категория:Язык программирования Си |
C++.