C++

Материал из Википедии — свободной энциклопедии
(перенаправлено с «Си плюс плюс»)
Перейти к навигации Перейти к поиску
C++
ISO C++ Logo.svg
Семантика мультипарадигмальный: объектно-ориентированное, обобщённое, процедурное, метапрограммирование
Класс языка объектно-ориентированный язык программирования, мультипарадигмальный язык программирования, процедурный язык программирования[d], язык функционального программирования, язык обобщённого программирования[d], язык программирования, free-form language[d] и компилируемый язык программирования
Тип исполнения компилируемый
Появился в 1983
Автор Страуструп, Бьёрн
Расширение файлов .cc, .cpp, .cxx, .c, .c++, .h, .hpp, .hh, .hxx или .h++
Система типов статическая
Основные реализации: GNU C++, Microsoft Visual C++, Intel C++ compiler, Open64 C++ Compiler, Clang, Comeau C/C++[en], Embarcadero C++ Builder, Watcom C++ compiler, Digital Mars C++, Oracle Solaris Studio C++ compiler, Turbo C++
Диалекты ISO/IEC 14882 C++
Испытал влияние Си, Симула, Алгол 68, Клу, ML и Ада
Сайт isocpp.org​ (англ.)
open-std.org/jtc1…

C++ (читается си-плюс-плюс[1][2]) — компилируемый, статически типизированный язык программирования общего назначения.

Поддерживает такие парадигмы программирования, как процедурное программирование, объектно-ориентированное программирование, обобщённое программирование. Язык имеет богатую стандартную библиотеку, которая включает в себя распространённые контейнеры и алгоритмы, ввод-вывод, регулярные выражения, поддержку многопоточности и другие возможности. C++ сочетает свойства как высокоуровневых, так и низкоуровневых языков.[3][4] В сравнении с его предшественником — языком C, — наибольшее внимание уделено поддержке объектно-ориентированного и обобщённого программирования.[4]

C++ широко используется для разработки программного обеспечения, являясь одним из самых популярных языков программирования[мнения 1][мнения 2]. Область его применения включает создание операционных систем, разнообразных прикладных программ, драйверов устройств, приложений для встраиваемых систем, высокопроизводительных серверов, а также развлекательных приложений (игр). Существует множество реализаций языка C++, как бесплатных, так и коммерческих и для различных платформ. Например, на платформе x86 это GCC, Visual C++, Intel C++ Compiler, Embarcadero (Borland) C++ Builder и другие. C++ оказал огромное влияние на другие языки программирования, в первую очередь на Java и C#.

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

Содержание

История[править | править код]

Исторический этап развития[5] Год
Язык BCPL 1966
Язык Би (оригинальная разработка Томпсона под UNIX) 1969
Язык Си 1972
Си с классами 1980
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
C++14 2014
C++17 2017

Создание[править | править код]

Язык возник в начале 1980-х годов, когда сотрудник фирмы Bell Labs Бьёрн Страуструп придумал ряд усовершенствований к языку C под собственные нужды. [6] Когда в конце 1970-х годов Страуструп начал работать в Bell Labs над задачами теории очередей (в приложении к моделированию телефонных вызовов), он обнаружил, что попытки применения существующих в то время языков моделирования оказываются неэффективными, а применение высокоэффективных машинных языков слишком сложно из-за их ограниченной выразительности. Так, язык Симула имеет такие возможности, которые были бы очень полезны для разработки большого программного обеспечения, но работает слишком медленно, а язык BCPL достаточно быстр, но слишком близок к языкам низкого уровня и не подходит для разработки большого программного обеспечения.

Вспомнив опыт своей диссертации, Страуструп решил дополнить язык C (преемник BCPL) возможностями, имеющимися в языке Симула. Язык C, будучи базовым языком системы UNIX, на которой работали компьютеры Bell, является быстрым, многофункциональным и переносимым. Страуструп добавил к нему возможность работы с классами и объектами. В результате практические задачи моделирования оказались доступными для решения как с точки зрения времени разработки (благодаря использованию Симула-подобных классов), так и с точки зрения времени вычислений (благодаря быстродействию C). В первую очередь в C были добавлены классы (с инкапсуляцией), наследование классов, строгая проверка типов, inline-функции и аргументы по умолчанию. Ранние версии языка, первоначально именовавшегося «C with classes» («Си с классами»), стали доступны с 1980 года.

Разрабатывая C с классами, Страуструп написал программу cfront[en] — транслятор, перерабатывающий исходный код C с классами в исходный код простого C. Это позволило работать над новым языком и использовать его на практике, применяя уже имеющуюся в UNIX инфраструктуру для разработки на C. Новый язык, неожиданно для автора, приобрёл большую популярность среди коллег и вскоре Страуструп уже не мог лично поддерживать его, отвечая на тысячи вопросов.

К 1983 году в язык были добавлены новые возможности, такие как виртуальные функции, перегрузка функций и операторов, ссылки, константы, пользовательский контроль над управлением свободной памятью, улучшенная проверка типов и новый стиль комментариев (//). Получившийся язык уже перестал быть просто дополненной версией классического C и был переименован из C с классами в «C++». Его первый коммерческий выпуск состоялся в октябре 1985 года.

До начала официальной стандартизации язык развивался в основном силами Страуструпа в ответ на запросы программистского сообщества. Функцию стандартных описаний языка выполняли написанные Страуструпом печатные работы по C++ (описание языка, справочное руководство и так далее). Лишь в 1998 году был ратифицирован международный стандарт языка C++: ISO/IEC 14882:1998 «Standard for the C++ Programming Language»; после принятия технических исправлений к стандарту в 2003 году — следующая версия этого стандарта — ISO/IEC 14882:2003.[7]

Развитие и стандартизация языка[править | править код]

В 1985 году вышло первое издание «Языка программирования C++», обеспечивающее первое описание этого языка, что было чрезвычайно важно из-за отсутствия официального стандарта. В 1989 году состоялся выход C++ версии 2.0. Его новые возможности включали множественное наследование, абстрактные классы, статические функции-члены, функции-константы и защищённые члены. В 1990 году вышло «Комментированное справочное руководство по C++», положенное впоследствии в основу стандарта. Последние обновления включали шаблоны, исключения, пространства имён, новые способы приведения типов и булевский тип.

Стандартная библиотека C++ также развивалась вместе с ним. Первым добавлением к стандартной библиотеке C++ стали потоки ввода-вывода, обеспечивающие средства для замены традиционных функций C printf и scanf. Позднее самым значительным развитием стандартной библиотеки стало включение в неё Стандартной библиотеки шаблонов.

В 1998 году был опубликован стандарт языка ISO/IEC 14882:1998 (известный как C++98),[8] разработанный комитетом по стандартизации C++ (ISO/IEC JTC1/SC22/WG21 working group). Стандарт C++ не описывает способы именования объектов, некоторые детали обработки исключений и другие возможности, связанные с деталями реализации, что делает несовместимым объектный код, созданный различными компиляторами. Однако для этого третьими лицами создано множество стандартов для конкретных архитектур и операционных систем.

В 2003 году был опубликован стандарт языка ISO/IEC 14882:2003, где были исправлены выявленные ошибки и недочёты предыдущей версии стандарта.

В 2005 году был выпущен отчёт Library Technical Report 1 (кратко называемый TR1). Не являясь официально частью стандарта, отчёт описывает расширения стандартной библиотеки, которые, как ожидалось авторами, должны быть включены в следующую версию языка C++. Степень поддержки TR1 улучшается почти во всех поддерживаемых компиляторах языка C++.

С 2009 года велась работа по обновлению предыдущего стандарта. Предварительной версией нового стандарта сперва был C++09, а спустя год — C++0x (впоследствии переименованный в C++11), куда были включены дополнения в ядро языка и расширение стандартной библиотеки, в том числе большую часть TR1.

C++ продолжает развиваться, чтобы отвечать современным требованиям. Одна из групп, разрабатывающих язык C++ и направляющих комитету по стандартизации C++ предложения по его улучшению — это Boost, которая занимается, в том числе, совершенствованием возможностей языка путём добавления в него особенностей метапрограммирования.

Никто не обладает правами на язык C++, он является свободным. Однако сам документ стандарта языка (за исключением черновиков) не доступен бесплатно.[9] В рамках процесса стандартизации, ISO выпускает несколько видов изданий. В частности, технические доклады и технические характеристики публикуются, когда «видно будущее, но нет немедленной возможности соглашения для публикации международного стандарта.» До 2011 года не было опубликовано три технических отчёта по C++: TR 19768: 2007 (также известный как C++, Технический отчёт 1) для расширений библиотеки в основном интегрирован в C++11, TR 29124: 2010 для специальных математических функций, и TR 24733: 2011 для десятичной арифметики с плавающей точкой. Техническая спецификация DTS 18822:. 2 014 (по файловой системе) была утверждена в начале 2015 года, и остальные технические характеристики находятся в стадии разработки и ожидают одобрения[10]

В марте 2016 года в России была создана рабочая группа РГ21 С++. Группа была организована для сбора предложений к стандарту C++, отправки их в комитет и защиты на общих собраниях Международной организации по стандартизации (ISO)[11].

История названия[править | править код]

Имя языка, получившееся в итоге, происходит от оператора унарного постфиксного инкремента C ++ (увеличение значения переменной на единицу). Имя C+ не было использовано потому, что является синтаксической ошибкой в C и, кроме того, это имя было занято другим языком. Язык также не был назван D, поскольку «является расширением C и не пытается устранять проблемы путём удаления элементов C».[6]

Философия C++[править | править код]

В книге «Дизайн и эволюция C++» [12] Бьёрн Страуструп описывает принципы, которых он придерживался при проектировании C++. Эти принципы объясняют, почему C++ именно такой, какой он есть. Некоторые из них:

  • Получить универсальный язык со статическими типами данных, эффективностью и переносимостью языка C.
  • Непосредственно и всесторонне поддерживать множество стилей программирования, в том числе процедурное программирование, абстракцию данных, объектно-ориентированное программирование и обобщённое программирование.
  • Дать программисту свободу выбора, даже если это даст ему возможность выбирать неправильно.
  • Максимально сохранить совместимость с C, тем самым делая возможным лёгкий переход от программирования на C.
  • Избежать разночтений между C и C++: любая конструкция, допустимая в обоих языках, должна в каждом из них обозначать одно и то же и приводить к одному и тому же поведению программы.
  • Избегать особенностей, которые зависят от платформы или не являются универсальными.
  • «Не платить за то, что не используется» — никакое языковое средство не должно приводить к снижению производительности программ, не использующих его.
  • Не требовать слишком усложнённой среды программирования.

Обзор языка[править | править код]

Стандарт C++ состоит из двух основных частей: описание ядра языка и описание стандартной библиотеки.

Первое время язык развивался вне формальных рамок, спонтанно, по мере встававших перед ним задач. Развитию языка сопутствовало развитие кросс-компилятора cfront. Новшества в языке отражались в изменении номера версии кросс-компилятора. Эти номера версий кросс-компилятора распространялись и на сам язык, но применительно к настоящему времени речь о версиях языка C++ не ведут. Лишь в 1998 году язык стал стандартизированным.

  • C++ поддерживает как комментарии в стиле C (/* комментарий */), так и однострочные (// вся оставшаяся часть строки является комментарием), где // обозначает начало комментария, а ближайший последующий символ новой строки, который не предварён символом \ (либо эквивалентным ему обозначением ??/), считается окончанием комментария. Плюс этого комментария в том, что его не обязательно заканчивать, то есть обозначать окончание комментария.
  • Спецификатор inline для функций. Функция, определённая внутри тела класса, является inline по умолчанию. Данный спецификатор является подсказкой компилятору и может встроить тело функции в код вместо её непосредственного вызова.
  • Квалификаторы const и volatile. В отличие от С, где const обозначает только доступ на чтение, в C++ переменная с квалификатором const должна быть инициализирована. volatile используется в описании переменных и информирует компилятор, что значение данной переменной может быть изменено способом, который компилятор не в состоянии отследить. Для переменных, объявленных volatile, компилятор не должен применять средства оптимизации, изменяющие положение переменной в памяти (например, помещающие её в регистр) или полагающиеся на неизменность значения переменной в промежутке между двумя присваиваниями ей значения. В многоядерной системе volatile помогает избегать барьеров памяти 2-го типа[источник не указан 2020 дней].
  • Пространства имён (namespace). Пример:
namespace Foo
{
   const int x=5;
}
const int y = Foo::x;

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

  • Для работы с памятью введены операторы new, new[], delete и delete[]. В отличие от библиотечных malloc и free, пришедших из C, данные операторы производят инициализацию объекта. Для классов это вызов конструктора, для POD типов инициализацию можно либо не проводить(new Pod;), либо провести инициализацию нулевыми значениями (new Pod(); new Pod{};).

Типы[править | править код]

В C++ доступны следующие встроенные типы. Типы C++ практически полностью повторяют типы данных в C:

  • Символьные: char, wchar_t (char16_t и char32_t, в стандарте C++11).
  • Целочисленные знаковые: signed char, short int, int, long intlong long, в стандарте C++11).
  • Целочисленные беззнаковые: unsigned char, unsigned short int, unsigned int, unsigned long intunsigned long long, в стандарте C++11).
  • С плавающей точкой: float, double, long double.
  • Логический: bool, имеющий значения true или false.

Операции сравнения возвращают тип bool. Выражения в скобках после if, while приводятся к типу bool.[13]

Язык ввёл понятие ссылок, а начиная с одиннадцатой версии стандарта rvalue ссылки и forwarding ссылки. (см. Ссылка (C++))

C++ добавляет к C объектно-ориентированные возможности. Он вводит классы, которые обеспечивают три самых важных свойства ООП: инкапсуляцию, наследование и полиморфизм.

В стандарте C++ под классом (class) подразумевается пользовательский тип, объявленный с использованием одного из ключевых слов class, struct или union, под структурой (structure) подразумевается класс, определённый через ключевое слово struct, и под объединением (union) подразумевается класс, определённый через ключевое слово union.

В теле определения класса можно указать как объявления функций, так и их определение. В последнем случае функция является встраиваемой (inline)). Нестатические функции-члены могут иметь квалификаторы const и volatile, а также ссылочный квалификатор (& или &&).

Наследование[править | править код]

C++ поддерживает множественное наследование. Базовые классы (классы-предки) указываются в заголовке описания класса, возможно, со спецификаторами видимости. Наследование от каждого класса может быть публичным, защищённым или закрытым:

Доступ члена базового класса/режим наследования private-член protected-член public-член
private-наследование недоступен private private
protected-наследование недоступен protected protected
public-наследование недоступен protected public

По умолчанию базовый класс наследуется как private.

В результате наследования класс-потомок получает все поля классов-предков и все их методы; можно сказать, что каждый экземпляр класса-потомка содержит подэкземпляр каждого из классов-предков. Если один класс-предок наследуется несколько раз (это возможно, если он является предком нескольких базовых классов создаваемого класса), то экземпляры класса-потомка будет включать столько же подэкземпляров данного класса-предка. Чтобы избежать такого эффекта, если он нежелателен, C++ поддерживает концепцию виртуального наследования. При наследовании базовый класс может объявляться виртуальным; на все виртуальные вхождения класса-предка в дерево наследования класса-потомка в потомке создаётся только один подэкземпляр.

Полиморфизм[править | править код]

C++ поддерживает динамический полиморфизм и параметрический полиморфизм.

Параметрический полиморфизм представлен:

  • Аргументами по умолчанию для функций. К примеру, для функции void f(int x, int y=5, int z=10), вызовы f(1), f(1,5) и f(1,5,10) эквивалентны.
  • Перегрузка функций: функция с одним именем может иметь разное число и разные по типу аргументы. Например :
    void Print(int x);
    void Print(double x);
    void Print(int x, int y);
    
Частным случаем перегрузки функций можно считать перегрузку операторов.
  • Механизмом шаблонов

Динамический полиморфизм реализуется с помощью виртуальных методов и иерархии наследования. Полиморфным в C++ является тип имеющий хотя бы один виртуальный метод. Пример иерархии:

class Figure
{
public:
    virtual void Draw() = 0; // чистый виртуальный метод
    virtual ~Figure();       // при наличии хотя бы одного виртуального метода деструктор следует сделать виртуальным
};

class Square : public Figure
{
public:
    void Draw() override;
};

class Circle : public Figure
{
public:
    void Draw() override;
};

Здесь класс Figure является абстрактным (и, даже, интерфейсным), так как метод Draw не определён. Объекты данного класса нельзя создать, зато можно использовать ссылки или указатели с типом Figure. Выбор реализации метода Draw будет производиться во время выполнения исходя из реального типа объекта.

Инкапсуляция[править | править код]

Инкапсуляция в C++ реализуется через указание уровня доступа к членам класса: они бывают публичными (открытыми, public), защищёнными (protected) и приватными (закрытыми, private). В C++ структуры формально отличаются от классов лишь тем, что по умолчанию уровень доступа к членам класса и тип наследования у структуры публичные, а у класса — приватные.

Доступ private protected public
Сам класс да да да
Друзья да да да
Наследники нет да да
Извне нет нет да

Проверка доступа происходит во время компиляции, попытка обращения к недоступному члену класса вызовет ошибку компиляции.

Друзья[править | править код]

Функции-друзья — это функции, не являющиеся функциями-членами и тем не менее имеющие доступ к защищённым и закрытым членам класса. Они должны быть объявлены в теле класса как friend. Например:

class Matrix {
    friend Matrix Multiply(Matrix m1, Matrix m2);
};

Здесь функция Multiply может обращаться к любым полям и функциям-членам класса Matrix.

Дружественным может быть объявлен как весь класс, так и функция-член класса. Четыре важных ограничения, накладываемых на отношения дружественности в C++:

  • Дружественность не транзитивна. Если A объявляет другом B, а B, в свою очередь, объявляет другом C, то C не становится автоматически другом для A. Для этого A должен явно объявить C своим другом.
  • Дружественность не взаимна. Если класс A объявляет другом класс B, то он не становится автоматически другом для B. Для этого должно существовать явное объявление дружественности A в классе B.
  • Дружественность не наследуется. Если A объявляет класс B своим другом, то потомки B не становятся автоматически друзьями A. Для этого каждый из них должен быть объявлен другом A в явной форме.
  • Дружественность не распространяется на потомков. Если класс A объявляет B другом, то B не становится автоматически другом для классов-потомков A. Каждый потомок, если это нужно, должен объявить B своим другом самостоятельно.

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

Специальные функции[править | править код]

Класс по умолчанию может иметь шесть специальных функций: конструктор по умолчанию, конструктор копирования, конструктор перемещения, деструктор, оператор присваивания копированием, оператор присваивания перемещением. Также можно явно определить их все (см. Правило трёх).

class Array {
public:
	Array() = default; // компилятор создаст конструктор по-умолчанию сам
	Array(size_t _len) :
			len(_len) {
		val = new double[_len];
	}
	Array(const Array & a) = delete;  // конструктор копирования явно удалён
	Array(Array && a); // конструктор перемещения
	~Array() {
		delete[] val;
	}
	Array& operator=(const Array& rhs); // оператор присваивания копированием
	Array& operator=(Array&& rhs); // оператор присваивания перемещением
	double& operator[](size_t i) {
		return val[i];
	}
	const double& operator[](size_t i) const {
		return val[i];
	}

protected:
	std::size_t len {0}; // инициализация поля
	double* val {nullptr};
};

Конструктор вызывается для инициализации объекта (соответствующего типа) при его создании, а деструктор — для уничтожения объекта. Класс может иметь несколько конструкторов, но деструктор может иметь только один. Конструкторы в C++ не могут быть объявлены виртуальными, а деструкторы — могут, и обычно объявляются для всех полиморфных типов, чтобы гарантировать правильное уничтожение доступного по ссылке или указателю объекта независимо от того, какого типа ссылка или указатель. При наличии хотя бы у одного из базовых классов виртуального деструктора, деструктор класса потомка автоматически становится виртуальным.

Шаблоны[править | править код]

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

template <typename T>
class Array {
    ...
    T& operator[](size_t i) {
		return val[i];
	}
protected:
	std::size_t len {0}; // инициализация поля
	T* val {nullptr};
};

Стандартная библиотека[править | править код]

Общая структура[править | править код]

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

Доступ к возможностям стандартной библиотеки C++ обеспечивается с помощью включения в программу (посредством директивы #include) соответствующих стандартных заголовочных файлов. Всего в стандарте C++11 определено 79 таких файлов. Средства стандартной библиотеки объявляются как входящие в пространство имён std. Заголовочные файлы, имена которых соответствуют шаблону «cX», где X — имя заголовочного файла стандартной библиотеки C без расширения (cstdlib, cstring, cstdio и пр.), содержат объявления, соответствующие данной части стандартной библиотеки C. Стандартные функции библиотеки C также находятся в пространстве имён std.

Состав[править | править код]

Стандартная библиотека включает в себя следующие разделы:

  • Поддержка языка. Включает средства, которые необходимы для работы программ, а также сведения об особенностях реализации. Выделение памяти, RTTI, базовые исключения, пределы значений для числовых типов данных, базовые средства взаимодействия со средой, такие как системные часы, обработка сигналов UNIX, завершение программы.
  • Стандартные контейнеры. В стандартную библиотеку входят шаблоны для следующих контейнеров: динамический массив(vector), статический массив(array), одно- и двунаправленные списки(list, forward_list), стек(stack), дек(deque), ассоциативные массивы(map, multimap), множества(set, multiset), очередь с приоритетом(priority_queue).
  • Основные утилиты. В этот раздел входит описание основных базовых элементов, применяемых в стандартной библиотеке, распределителей памяти и поддержка времени и даты в стиле C.
  • Итераторы. Обеспечивают шаблоны итераторов, с помощью которых в стандартной библиотеке реализуется стандартный механизм группового применения алгоритмов обработки данных к элементам контейнеров.
  • Алгоритмы. Шаблоны для описания операций обработки, которые с помощью механизмов стандартной библиотеки могут применяться к любой последовательности элементов, в том числе к элементам в контейнерах. Также в этот раздел входят описания функций bsearch() и qsort() из стандартной библиотеки C.
  • Строки. Шаблоны строк в стиле C++. Также в этот раздел попадает часть библиотек для работы со строками и символами в стиле C.
  • Ввод-вывод. Шаблоны и вспомогательные классы для потоков ввода-вывода общего вида, строкового ввода-вывода, манипуляторы (средства управления форматом потокового ввода-вывода в стиле C++).
  • Локализация. Определения, используемые для поддержки национальных особенностей и форматов представления (дат, валют и т. д.) в стиле C++ и в стиле C.
  • Диагностика. Определения ряда исключений и механизмов проверки утверждений во время выполнения (assert). Поддержка обработки ошибок в стиле C.
  • Числа. Определения для работы с комплексными числами, математическими векторами, поддержка общих математических функций, генератор случайных чисел.

Контейнеры, строки, алгоритмы, итераторы и основные утилиты, за исключением заимствований из библиотеки C, собирательно называются STL (Standard Template Library — стандартная шаблонная библиотека). Изначально эта библиотека была отдельным продуктом и её аббревиатура расшифровывалась иначе, но потом она вошла в стандартную библиотеку C++ в качестве неотъемлемого элемента. В названии отражено то, что для реализации средств общего вида (контейнеров, строк, алгоритмов) использованы механизмы обобщённого программирования (шаблоны C++ — template). В работах Страуструпа подробно описываются причины, по которым был сделан именно такой выбор. Основными из них являются бо́льшая универсальность выбранного решения (шаблонные контейнеры, в отличие от объектных, могут легко использоваться для не объектных типов и не требуют наличия общего предка у типов элементов) и его техническая эффективность (как правило, операции шаблонного контейнера не требуют вызовов виртуальных функций и могут легко встраиваться (inline), что в итоге даёт выигрыш в производительности).

Начиная со стандарта C++11 добавились следующие возможности:

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

Отличия от языка C[править | править код]

Совместимость с языком С[править | править код]

Выбор именно C в качестве базы для создания нового языка программирования объясняется тем, что язык C:

  1. является многоцелевым, лаконичным и относительно низкоуровневым языком;
  2. подходит для решения большинства системных задач;
  3. исполняется везде и на всём;
  4. стыкуется со средой программирования UNIX.

— Б. Страуструп. Язык программирования C++. Раздел 1.6[14]

Несмотря на ряд известных недостатков языка C, Страуструп пошёл на его использование в качестве основы, так как «в C есть свои проблемы, но их имел бы и разработанный с нуля язык, а проблемы C нам известны». Кроме того, это позволило быстро получить прототип компилятора (cfront[en]), который лишь выполнял трансляцию добавленных синтаксических элементов в оригинальный язык C.

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

  • сохранение действующего кода, написанного изначально на C и прямо перенесённого в C++;
  • исключение необходимости переучивания программистов, ранее изучавших 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 и не включает его в себя. Существует и такой верный для 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;
}

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

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

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

Реализации[править | править код]

STL до включения в стандарт C++ была сторонней разработкой, вначале — фирмы HP, а затем SGI. Стандарт языка не называет её «STL», так как эта библиотека стала неотъемлемой частью языка, однако многие люди до сих пор используют это название, чтобы отличать её от остальной части стандартной библиотеки (потоки ввода-вывода (iostream), подраздел C и другие).

Проект под названием STLport[17], основанный на SGI STL, осуществляет постоянное обновление STL, IOstream и строковых классов. Некоторые другие проекты также занимаются разработкой частных применений стандартной библиотеки.

Дальнейшее развитие[править | править код]

Текущий стандарт языка ISO/IEC 14882:2017 был опубликован в декабре 2017 года. Неофициально его обозначают как C++17. Следующая версия стандарта, запланированная на 2020 год, имеет неофициальное обозначение C++20.

Общие направления развития C++[править | править код]

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

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

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

  • Явно определяемые константные функции и выражения constexpr.
  • Универсальная инициализация.
  • Конструкторы и операторы присваивания с семантикой переноса.
  • Вывод типов.
Для применения в шаблонах, там, где затруднительно указать конкретный тип переменной, введены два новых механизма: переменные типа auto и описание decltype.
  • Цикл по коллекции.
Вслед за многими современными языками в C++ введена конструкция «цикл по коллекции» вида for(type &x : array){...}. Здесь тело цикла выполняется для каждого элемента коллекции array, а x в каждой итерации будет ссылаться на очередной элемент коллекции. В качестве коллекции может выступать C-массив или любой контейнер стандартной библиотеки, для которого определены итераторы begin и end.
  • Лямбда-выражения.
Добавлена возможность объявлять лямбда-выражения (безымянные функции, определяемые в точке применения), в том числе зависящие от внешних переменных (замыкания). Лямбда-выражения могут присваиваться переменным и использоваться везде, где требуется функция соответствующего типа, например, в алгоритмах стандартной библиотеки.
  • Изменения в описании виртуальных методов.
Добавлен необязательный модификатор override, который употребляется в объявлении метода, замещающего виртуальный метод родительского класса. Описание замещения с override вызывает проверку на наличие в родительском классе замещаемого метода и на совпадение сигнатур методов.
Добавлен также модификатор final, как и в Java, запрещающий дальнейшее замещение помеченного им метода. Также final может быть объявлен класс — в таком случае от него запрещено наследовать новые классы.
Определено ключевое слово для константы — нулевого указателя: nullptr.
Внесены изменения в семантику и, частично, синтаксис перечислений и объединений. Добавлена возможность создавать типобезопасные перечисления, с объединений снят ряд ограничений на структуру.
От компилятора требуется правильный лексический разбор текста программы с несколькими закрывающимися угловыми скобками подряд (ранее последовательность «>>» воспринималась однозначно как операция побитового сдвига вправо, поэтому в записи вложенных шаблонных конструкций требовалось обязательно разделять знаки «больше» пробелами или переводами строк).

Примеры программ[править | править код]

Пример № 1[править | править код]

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

#include <iostream>

int main()
{
    std::cout << "Hello, world!\n";
    return 0;
}

Пример № 2[править | править код]

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

#include <iostream> // для использования std::cout
#include <vector> // содержит динамический массив
#include <map> // содержит тип данных словарь
#include <string>

int main()
{
    // импортируем все объявления в пространстве имён "std" в глобальное пространство имён.
    using namespace std;
    // Объявляем ассоциативный контейнер со строковыми ключами и данными в виде векторов строк
    map< string, vector<string> > items;

    // Добавим в этот ассоциативный контейнер пару человек и дадим им несколько предметов
    items["Anya"].push_back("scarf");
    items["Dmitry"].push_back("tickets");
    items["Anya"].push_back("puppy");

   // Переберём все объекты в контейнере
    for(const auto& person : items) {
        // person - это пара двух объектов: person.first - это его имя,
        // person.second - это список его предметов (вектор строк)
        cout << person.first << " is carrying " << person.second.size() << " items" << endl;
    }
}

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

#include <vector>

int main()
{
    using std::vector;
    vector<int> my_vector;
}

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

Достоинства и недостатки[править | править код]

Достоинства[править | править код]

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

  • Высокая совместимость с языком Си : код на Си может быть с минимальными переделками скомпилирован компилятором C++. Внешнеязыковой интерфейс является прозрачным, так что библиотеки на Си могут вызываться из C++ без дополнительных затрат, и более того — при определённых ограничениях код на C++ может экспортироваться внешне не отличимо от кода на Си (конструкция extern "C").
  • Как следствие предыдущего пункта — вычислительная производительность. Язык спроектирован так, чтобы дать программисту максимальный контроль над всеми аспектами структуры и порядка исполнения программы. Один из базовых принципов C++ — «не платишь за то, что не используешь» (см. Философия C++) — то есть ни одна из языковых возможностей, приводящая к дополнительным накладным расходам, не является обязательной для использования. Имеется возможность работы с памятью на низком уровне.
  • Поддержка различных стилей программирования: традиционное императивное программирование (структурное, объектно-ориентированное), обобщённое программирование, функциональное программирование, порождающее метапрограммирование.
  • Автоматический вызов деструкторов объектов в адекватном порядке (обратном вызову конструкторов) упрощает и повышает надёжность управления памятью и другими ресурсами (открытыми файлами, сетевыми соединениями, соединениями с базами данных и т. п.).
  • Перегрузка операторов позволяет кратко и ёмко записывать выражения над пользовательскими типами в естественной алгебраической форме.
  • Имеется возможность управления константностью объектов (модификаторы const, mutable, volatile). Использование константных объектов повышает надёжность и служит подсказкой для оптимизации. Перегрузка функций-членов по признаку константности позволяет определять выбор метода в зависимости цели вызова (константный для чтения, неконстантный для изменения). Объявление mutable позволяет сохранять логическую константность при виде извне кода, использующего кэши и ленивые вычисления.
  • Шаблоны C++ дают возможность построения обобщённых контейнеров и алгоритмов для разных типов данных. Попутно шаблоны дают возможность производить вычисления на этапе компиляции.
  • Возможность встраивания предметно-ориентированных языков программирования в основной код. Такой подход использует, например библиотека Boost.Spirit, позволяющая задавать EBNF-грамматику парсеров прямо в коде C++.
  • Доступность. Для C++ существует огромное количество учебной литературы, переведённой на всевозможные языки. Язык имеет высокий порог вхождения, но среди всех языков такого рода обладает наиболее широкими возможностями.

Недостатки[править | править код]

К числу обычно упоминаемых недостатков языка можно отнести:

  • Отсутствие системы модулей. C++ унаследовал от Си подключение заголовочных файлов с помощью препроцессора. Это вынуждает дублировать описания объектов, порождает неочевидные требования к коду (см. правило одного определения) и увеличивает объём компилируемого текста, а значит и время компиляции.
  • Наличие более чем одного механизма для выполнения одних и тех же задач, что усложняет язык и приводит к неоптимальному и небезопасному кодированию.
  • Унаследованные от Си опасные и провоцирующие ошибки возможности, такие как макроопределения #define, адресная арифметика и неявное приведение типов.
  • Возможность прямого управления распределением памяти провоцирует ошибки, приводящие к внезапному краху программ из-за разрушения стека или обращения к невыделенной памяти.
  • Шаблоны порождают объёмный код. Появившиеся в поздних версиях языка возможности частичного определения шаблонов сильно усложняют язык.
  • Объектно-ориентированная подсистема построена так, что оказывается затруднительно применять многие приёмы, обычные для других объектных языков.
  • Множественное (в том числе виртуальное) наследование усложняет транслятор и приводит к созданию громоздких иерархий классов, которые при любом изменении требований к программе могут потребовать серьёзного пересмотра.
  • Сложный синтаксис и объёмная спецификация языка затрудняют его изучение.
  • Язык не поощряет создание надёжного, легко читаемого и удобного в сопровождении кода, вместо этого зачастую предлагая выбор между короткими и простыми, но опасными средствами, унаследованными от Си, и новыми, объёмными и сложными, но более безопасными механизмами.
  • Сложная и постоянно разрастающаяся стандартная библиотека, затрудняющая изучение и дополнительно увеличивающая объём программ.
  • Отсутствие или ограниченность поддержки ряда полезных технологий и методик программирования.

Критика[править | править код]

О критике C++ в целом[править | править код]

С одной стороны, C++ является потомком Симулы, которую Алан Кэй определил[21] как «Алгол с классами», и потому актуальна оценка C++ в сравнении с другими языками из семейства потомков Алгола (Pascal, Java, C#, Visual Basic, Delphi, D, Oberon и пр.). С другой стороны, C++ претендует на мультипарадигменность и универсальную применимость (в отличие от Си, ориентированного на очень узкий круг задач), и используется в промышленности намного шире других потомков Алгола, потому представляет интерес оценка C++ в сравнении со всем многообразием применяемых языков, включая и Си. Во избежание повторений, оценки обычно совмещаются.

C++ — язык, складывающийся эволюционно. В отличие от языков с формальным определением семантики, каждый элемент C++ заимствовался из других языков отдельно и независимо от остальных элементов (ничто из предложенного C++ за всю историю его развития не было новшеством в Computer Science), что сделало язык чрезвычайно сложным, со множеством дублирующихся и взаимно противоречивых элементов, блоки которых основаны на разных формальных базах. В этом отношении C++ повторяет путь PL/1, но, в отличие от последнего, длительное повсеместное использование C++ обеспечил выбор языка Си в качестве отправной точки.

За редкими исключениями, критики C++ не противопоставляют ему какой-либо другой конкретный язык, а утверждают, что для всякого случая применения C++ существует альтернативный инструментарий, позволяющий решить ту же задачу более эффективно и качественно. Отдельно подвергается критике принцип совместимости с Си, результатом следования которому является заимствование целого ряда опасных и провоцирующих ошибки решений.

В свою очередь, сторонники C++ возражают против сравнения различных аспектов C++ с различными языками, заявляя, что общий набор средств C++ существенно шире, чем в большинстве альтернативных языков, и эта широта является оправданием несовершенства каждой отдельно взятой возможности; в том числе недостатки, вызванные сохранением совместимости с Си, оправданы предоставляемыми ею преимуществами (см. раздел Философия C++).

Критики указывают на тот подтверждённый исследованиями[22][23] факт, что использование наиболее подходящего языка или декомпозиция проекта на подзадачи, решаемые на наиболее подходящих языках, сокращает на порядок общую трудоёмкость разработки при одновременном повышении на порядки основных показателей качества программирования, из чего делается вывод, что если задача требует одновременно высоко- и низкоуровневых возможностей, то разумнее использовать Си совместно с высокоуровневыми языками.

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

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

Критика отдельных элементов и концепций[править | править код]

Контроль за поведением[править | править код]

Принцип C++ «не платишь за то, что не используешь» (см. Философия C++) заявляется как средство обеспечения высокой скорости исполнения. На практике он лишь приводит к чрезмерному использованию ситуативного (ad hoc) полиморфизма — явного описания различного поведения даже для редких ситуаций под единым идентификатором — то есть перегрузки функций. В C++ предусмотрено сразу три формы перегрузки, что приводит к значительному дублированию кода. Перегрузка операторов призвана дать возможность введения в программу т. н. «синтаксического сахара», но в C++ может поощрять бесконтрольное изменение поведения элементарных операций, в том числе new/delete и new[]/delete[], для разных типов (что резко повышает риск разного рода ошибок). Это обусловлено, во-первых, тем, что вводить новый синтаксис нельзя (хотя синтаксис стандартных операторов C++ адекватен семантике далеко не всех типов, которые может потребоваться ввести в программу); а во-вторых, тем, что всякий учебник даже для низкого порога вхождения показывает, как перегружать те или иные операторы, включая и управление временем жизни объектов. (Обычно ввод специального синтаксиса для операций является совершенно безболезненным в аппликативных языках, где эта возможность существует независимо от полиморфной семантики системы типов и не предоставляет доступа к управлению памятью.) Некоторые интуитивно ожидаемые операции (подчистка динамических объектов в случае генерации исключений) в C++ не выполняются в соответствии с означенным принципом; в то же время, значительная часть перегруженных функций и операторов вызывается неявно (приведение типов, создание временных экземпляров классов и др.). Попутно идеология языка смешивает «контроль за поведением» с «контролем за эффективностью» — что представляет опасность, так как де-факто возможности явного контроля этих аспектов исполнения программы со стороны человека являются взаимоисключающими[пояснения 1]. В сочетании с изобилием побочных эффектов всё это приводит к тому, что по мере роста сложности системы код на C++ не абстрагируется, а, наоборот, усложняется, и значительно снижаются показатели понимаемости и тестируемости — возникает необходимость контролировать (как чтением, так и отладкой) слои реализации по разные стороны от текущего барьера абстракции, что считается плохой практикой в программировании. В результате трудоёмкость (а значит, и стоимость) разработки растёт от объёма реализованной функциональности по вогнутому закону (в языках с полиморфной семантикой системы типов этот рост характеризуется выпуклой кривой за счёт существенно более высокого показателя повторного использования кода).

Компонентное и объектно-ориентированное программирование[править | править код]

Объектная модель C++, унаследованная от Симулы и дополненная двумя формами множественного наследования (простой и виртуальной), имеет не только объективные проблемы, но и опасную идеологию. По словам создателя Smalltalk Алана Кэя, объектная модель «Алгол с классами» обладает худшими характеристиками качества, чем модель «всё — объект»[21], и в этом смысле C++ уступает своему ближайшему конкуренту Objective-C: показатель повторного использования кода оказывается крайне низким (см. раздел Полиморфизм); рефлексивное метапрограммирование — невозможным; показатели понимаемости, модифицируемости и тестируемости — слабыми (см. раздел Отсутствие возможностей). Реализация указателей на методы классов не стандартизирована, и их размер в различных компиляторах варьируется от диапазоне 4 до 20 байт, что значительно снижает портируемость программ с их использованием[24].

Принятая в сообществе C++ методология декомпозиции приводит к проектным решениям, не доказуемым математически и неадекватным предметной области, и потому угрожающим неоправданными затратами и скрытыми ошибками. Традиционно понятие "тип" («класс») отождествляется с понятием «множества» (или реже «категории»). Понятие «наследования классов» в информатике традиционно означает создание «подклассов», то есть «подмножеств». В C++ обычной практикой является применение наследования вместо вложения: определение нового типа на основании более простых взаимно-ортогональных (а значит, не имеющих точек соприкосновения) типов осуществляется посредством создания их общего подтипа (условно говоря, класс "автомобиль" создаётся путём наследования классов "колесо" и "двигатель"). Например, в [25] приводится учебно-рекомендательный пример реализации класса «список» как подкласса от класса «элемент списка», который, хотя и заявляется как скалярный тип (контейнер для единственного значения), тем не менее содержит операции, присущие агрегатным типам (функции доступа к другим элементам списка, внешним по отношению к текущему объекту); при этом он не может использоваться как агрегатный тип и вообще не предназначен для самостоятельного использования. Такое отношение типов является абсурдом с точки зрения математики и невоспроизводимо на более строгих языках.

Идеология некоторых библиотек опирается на возможность приведения типов вверх и вниз по иерархии классов (операции static_cast и dynamic_cast), подтверждая, что типобезопасность не входит в традиции языка. Ошибочность проектных решений, принятых в соответствии с этой идеологией, может обнаруживаться на поздних этапах разработки и из-за высокой вязкости требовать повторной разработки значительных частей проекта. Ярким примером является описанный в[22] случай:

Пример подобной проблемы описан в [C.Potts, "Software-Engineering Research Revisited, " IEEE Software (Sept., 1993)] и [M.Lubers, C.Potts & C.Richter, "Developing Initial OOA Models, " Proc. Intl. Conf. Software Eng., Los Alamitos, Calif. (1992)]. Для исследования применимости объектно-ориентированной декомпозиции к системам разного рода были проанализированы три случая. Один из них был реальным проектом разработки системы наведения ракеты Томагавк. Разработчики сочли, что все ракеты можно разделить на подклассы в соответствии с видом боеголовки и навигационными характеристиками. Также их можно разделить на тактические и учебные. Эти две таксономии были практически ортогональны, и было принято решение использовать множественное наследование для применения этих категорий к конкретным видам ракет. Однако, на более поздней стадии разработки было обнаружено, что ортогональность нарушена: хотя некоторые виды учебных ракет могут нести боеголовки, ядерные ракеты не могут. Продолжение принятого подхода к построению архитектуры привело бы к неэлегантным и искусственным моделям, скрывающим под массой деталей исключительные ситуации, которые легко упустить.

— Martin Ward в [22]

Как отмечает Ян Джойнер[26], C++ ошибочно отождествляет инкапсуляцию и сокрытие:

Существует большая путаница относительно инкапсуляции, основным источником которой является C++, отождествляющий инкапсуляцию с сокрытием данных. Словарь Маккуори определяет глагол инкапсулировать как «помещать в прямом или переносном смысле в капсулу». Объектно-ориентированное понимание инкапсуляции заключается в том, чтобы помещать родственные данные, процедуры и определения в капсулу класса. Это не обязательно означает сокрытие. Сокрытие реализации является ортогональным понятием, которое становится возможным благодаря инкапсуляции. … Инкапсуляция предоставляет возможность отделять абстрактный интерфейс класса от его реализации: интерфейс — это видимая поверхность капсулы, реализация скрыта в капсуле. … Сокрытие реализации означает, что данными можно манипулировать, изменяя их, только внутри класса, но не означает сокрытие интерфейсных данных. … Для сокрытия реализации в C++ необходимо осуществлять доступ к данным через функции Си. Это называется сокрытием данных в C++. … Механизм доступа является скрываемой деталью реализации. C++ обеспечивает заметные различия в механизмах доступа к константам, переменным и функциям. … Большинство не-Си-подобных языков обеспечивают единый механизм доступа к константам, переменным и процедурам, возвращающим значения.

— Ian Joyner в [26]

При этом совпадение типов определяется на уровне идентификаторов, а не сигнатур; как следствие, оказывается невозможно подменять модули (классы), основываясь на совпадении их интерфейсной функциональности. То есть если некоторые возможности отсутствуют в C++, но реализованы на уровне библиотек (такие как сборка мусора или длинная арифметика), то для получения выгоды от них необходимо вручную модифицировать уже имеющийся код для адаптации его под новый модуль. В языках самых разных семантик (ML, Smalltalk, Erlang, Python, и даже Си) полиморфизм, инкапсуляция и сокрытие являются независимыми (ортогональными) категориями, а контроль типов осуществляется по соответствию их сигнатур, а не идентификаторов,— так что выгода от нового кода обеспечивается простой подменой имеющегося модуля на новый, что и называется абстракцией[27]. В C++, как отмечает Линус Торвальдс[28], код кажется абстрактным лишь до тех пор, пока не возникает необходимость его изменить. Результирующий ущерб от совокупности описанных свойств отмечается также Линусом Торвальдсом[мнения 3]: C++ провоцирует на написание в дополнение к структурам и функциям Си значительного объёма кода, не имеющего принципиального значения с т.з. функциональности программы.

Критика C++ с позиций только ООП (без сравнения методологий проектирования) с описанием вреда от влияния C++ на другие языки приведена в работе[26].

Кроссплатформенность[править | править код]

C++ заявляется как кроссплатформенный: стандарт языка накладывает минимальные требования на ЭВМ для запуска скомпилированных программ. На практике для написания портируемого кода на C++ требуется огромное мастерство и опыт, и «небрежные» коды на C++ с высокой вероятностью могут оказаться непортируемыми[29]. Тонкое владение C++ в принципе может сделать код на C++ столь же портируемым, что и код на Си (хотя, по мнению Линуса Торвальдса, C++ при этом фактически сократится до своего подмножества Си[28]). Однако, критики C++ утверждают, что изучение и использование одновременно всех языков, противопоставляемых C++ (не вызывающих серьёзных проблем при портировании), в сумме требует примерно тех же интеллекта, усилий и временных затрат, что и изучение и использование одного только C++ на высококлассном уровне — в связи с чем становится актуальной также оценка порога вхождения и результативности (производительности и качества труда программистов).

Многие аспекты в спорах «за и против C++» обусловлены расхождением в сущностном понимании процесса стандартизации. Б.Страуструп и его последователи считают, что «стандарт — это контракт между программистами, разрабатывающими программы на языке, и программистами, разрабатывающими компиляторы языка»[12]. Каждый новый стандарт C++ являлся декларацией того, что отныне должно быть реализовано во всех компиляторах — при том, что C++ имеет естественное определение семантики, то есть потенциально зависим от реализации (стандарт содержит множество пунктов, определённых как «implementation-defined»).

Традиционно успешная стандартизация в технике представляет собой формальный перевод стандарта из статуса де-факто в статус де-юре (подытоживание общепринятых, устоявшихся знаний для обеспечения надёжной внутриотраслевой совместимости). Теоретически эти определения стандартизации тождественны. На практике определение Страуструпа согласуется с традиционным лишь при условии, что нет абсолютно никаких препятствий против немедленного и беспрекословного подчинения статуса де-факто декларированному де-юре — то есть если абсолютно все компиляторы реализуют поддержку нового стандарта сразу после его выхода и со 100 % соответствием ему, и что новый стандарт не отменяет никаких положений старого, кроме признанных убыточными или вредоносными. Так может происходить лишь в условиях полного отсутствия человеческого фактора (иначе говоря, при отсутствии программистов как таковых). На практике именно этим и обусловлено значительное отставание реальной кроссплатформенности C++ от заявленной разработчиками стандарта и противоречивость предлагаемых возможностей. Сторонники C++ считают взгляд Страуструпа на стандартизацию более практико-ориентированным и принимают порождаемые им проблемы за должное.

Все противопоставляемые C++ языки программирования, если проходили процедуру стандартизации (что не обязательно для портируемости), то лишь после многолетней апробации на практике; при этом все они изначально имеют формальное определение семантики, так что накапливающиеся к моменту стандартизации изменения от первой версии языка оказываются не принципиальны.

Отсутствие возможностей[править | править код]

Рефлексивное метапрограммирование[править | править код]

Рефлексивное метапрограммирование в C++ невозможно. Интроспекция предусмотрена, но реализована отдельно от основной системы типов, что делает её практически бесполезной. Наибольшее, что можно получить — параметризацию поведения на заранее известном диапазоне случаев. Это является препятствием против применения C++ в большинстве подходов к реализации Искусственного Интеллекта (за исключением самых примитивных, реализующих статический алгоритм).

Порождающее метапрограммирование[править | править код]

Порождающее метапрограммирование на основе шаблонов C++ трудоёмко и ограничено по возможностям. Оно осуществляется за счёт реализации статического (исполняемого на этапе компиляции) интерпретатора примитивного функционального языка программирования посредством шаблонов C++, а также примитивного препроцессора, унаследованного от Си. Сама по себе данная возможность весьма привлекательна, но, во-первых, код на шаблонах C++ имеет крайне низкие показатели понимаемости и тестируемости; во-вторых, при разворачивании шаблонов порождается неэффективный код, так как язык шаблонов не предоставляет никаких средств для оптимизации (см. также раздел #Вычислительная эффективность). При этом встраиваемые предметно-специфичные языки, реализуемые таким образом, всё равно требуют знания самого C++ для их использования, так как возводимые барьеры абстракции сохраняют все свойства нижележащей реализации, что не обеспечивает полноценное разделение труда. Таким образом, возможности C++ по расширению возможностей самого C++ весьма ограничены.[30][31]

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

Функциональное программирование[править | править код]

Явная поддержка функционального программирования присутствует только в стандарте C++0x, вышедшим лишь после почти 30 лет развития языка. До этого данный пробел устранялся различными библиотеками (Loki, Boost), использующими язык шаблонов для расширения основного языка функциональными конструкциями. Качество подобных решений значительно уступает качеству встроенных в функциональные языки решений[пояснения 2] и качеству реализаций высокоуровневых возможностей C++ (таких как ООП) посредством функциональных языков. Все реализованные в C++ возможности ФП оказываются лишь их эмуляцией и используются совместно с императивными возможностями, что не даёт возможности применения присущих функциональному программированию мощных оптимизационных методик (см. раздел #Вычислительная эффективность). Кроме того, из-за трудоёмкости использования шаблонов, на практике ФП в C++ обычно ограничивается вызовами функциональных библиотек и реализацией отдельных методов, и практически не даёт преимуществ в проектировании программ (см. Соответствие Карри — Ховарда и пред.пункт), так что оно в C++ по-прежнему осуществляется обычно лишь посредством объектно-ориентированной декомпозиции.

Избыточные и опасные возможности[править | править код]

Гарантия некорректности[править | править код]

Из-за слабой системы типов программист оказывается волен легко нарушить заданную в конкретном случае дисциплину программирования. Например, хотя модификатор const предназначен для повышения предсказуемости поведения (что должно облегчить доказательство корректности, и, как следствие, расширить возможности оптимизации), но модификатор mutable предназначен именно для принудительного разрешения изменения состояния внутри константного объекта. Это значит, что код сторонней библиотеки на C++ может содержать изменяемое состояние вопреки любым попыткам назначить ему извне свойство константности. Более того, допускается динамически удалить атрибут const с константного объекта, превращая его в леводопустимый (L-value). Сама по себе явная декларация в спецификации языка подобных возможностей делает попытки формальной верификации бессмысленными.

Неконтролируемая макроподстановка[править | править код]

Средства макроподстановки Си (#define) являются сколь мощным, столь же опасным средством. Они сохранены в C++ несмотря на то, что для решения всех[источник не указан 1924 дня] задач, для которых они были предусмотрены в Си, в C++ были предоставлены более строгие и специализированные средства — шаблоны, перегрузка функций, inline-функции, пространства имён, более развитая типизация, расширение применения модификатора const, и др. В унаследованных от Си стандартных библиотеках много потенциально опасных макросов.[32] Шаблонное метапрограммирование также порой совмещается с использованием макроподстановки для обеспечения т. н. «синтаксического сахара».

Вычислительная эффективность[править | править код]

Результирующий объём исполнимого кода[править | править код]

Использование шаблонов C++ представляет собой параметрический полиморфизм на уровне исходного кода, но при трансляции он превращается в ситуативный (ad hoc) полиморфизм (то есть перегрузку функций), что приводит к существенному увеличению объёма машинного кода в сравнении с языками, имеющими истинно полиморфную систему типов (потомками ML). Для снижения размера машинного кода пытаются автоматически обрабатывать исходный код до этапа раскрутки шаблонов[33][34]. Другим решением могла бы быть стандартизованная ещё в 1998 году возможность экспорта шаблонов, но она доступна далеко не во всех компиляторах, так как её трудно реализовать[35][36][мнения 4] и для импорта библиотек шаблонов C++ в языки с существенно отличной от C++ семантикой она всё равно была бы бесполезна. Сторонники C++ оспаривают масштабы раздувания кода как преувеличенные[37], игнорируя даже тот факт, что в Си параметрический полиморфизм транслируется непосредственно, то есть без дублирования тел функций вообще. При этом сторонники C++ считают, что параметрический полиморфизм в Си опасен — то есть более опасен, чем переход от Си к C++ (противники C++ утверждают обратное — см. выше).

Потенциал оптимизации[править | править код]

Из-за слабой системы типов и изобилия побочных эффектов становится крайне затруднительным эквивалентное преобразование программ, а значит и встраивание в компилятор многих оптимизирующих алгоритмов, таких как автоматическое распараллеливание программ, удаление общих подвыражений, λ-подъём, вызовы процедур с передачей продолжений, суперкомпиляция и др. В результате реальная эффективность программ на C++ ограничивается имеющейся квалификацией программистов и вложенными в конкретный проект усилиями, и «небрежная» реализация может существенно уступать по эффективности «небрежным» реализациям на языках более высокого уровня, что подтверждается сравнительными испытаниями языков[38]. Это является существенным препятствием против применения C++ в индустрии data mining.

Эффективное управление памятью[править | править код]

Потенциал к повышению эффективности управления памятью весьма ограничен. Хотя существуют многочисленные реализации автоматической сборки мусора, использование более эффективных способов управления памятью, таких как статический вывод регионов (англ. region-based memory management) для конкретной библиотеки невозможно (точнее, это привело бы к реализации поверх языка C++ интерпретатора нового языка, сильно отличающегося от C++ как большинством объективных свойств, так и общей идеологией) по причине необходимости прямого доступа к AST. Для автоматического управления памятью в C++ традиционно используются т. н. «умные указатели», ручное же управление памятью снижает эффективность самих программистов (см. раздел Результативность).

Замедление Си[править | править код]

Противники C++ утверждают, что в данном случае расширение возможностей языка привело к серьёзному ухудшению его качества, из-за чего он не может эффективно использоваться как средство низкоуровневого программирования. В контексте задач, для решения которых разработан Си, в частности, условиях сильно ограниченных вычислительных ресурсов (например, при программировании многих встраиваемых систем), неприемлемыми могут оказаться самые разные аспекты C++. Так, механизм виртуальных функций реализуется посредством позднего связывания (англ. late binding), то есть требует динамического вычисления реального адреса функции (RVA), шаблоны (использование которых неизбежно при кодировании в стиле C++, так как на них основана стандартная библиотека) приводят к раздуванию кода и ухудшению возможностей оптимизации. Наконец, система может просто не располагать тем объёмом памяти, который необходим для библиотеки времени исполнения (RTL). Хотя формально стандарт не накладывает ограничений на состав этой библиотеки для конкретной программы на C++, при полном её удалении большинство конструкций языка, связанных с RTL, окажутся недоступными (например, операции new/delete и new[]/delete[]), в результате C++ сократится до Си или его подмножества, что делает бессмысленным его использование.

Линус Торвальдс, говоря о выборе между C++ и Си в низкоуровневых задачах, замечает:

C++ приводит к очень, очень плохим проектным решениям. Неизбежно начинают применяться «замечательные» библиотечные возможности вроде STL, и Boost, и прочего мусора, которые могут «помочь» программированию, но порождают:
 — невыносимую боль, когда они не работают (и всякий, кто утверждает, что STL и особенно Boost стабильны и портируемы, настолько погряз во лжи, что это даже не смешно)
 — неэффективно абстрагированные программные модели, когда спустя два года обнаруживается, что какая-то абстракция была недостаточно эффективна, но теперь весь код зависит ото всех окружающих её замечательных объектных моделей, и её нельзя исправить, не переписав всё приложение.
Другими словами, единственный способ иметь хороший, эффективный, низкоуровневый и портируемый C++ сводится к тому, чтобы ограничиться всеми теми вещами, которые элементарно доступны в Си. А ограничение проекта рамками Си будет означать, что люди его не выкинут, и что будет доступно множество программистов, действительно хорошо понимающих низкоуровневые особенности и не отказывающихся от них из-за идиотской ерунды про «объектные модели».
… когда эффективность является первостепенным требованием, «преимущества» C++ будут огромной ошибкой.

Результативность[править | править код]

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

Ручное управление памятью[править | править код]

Как отмечается в исследовании[39], программисты на Си тратят 30 % — 40 % общего времени разработки (не считая отладки) только на управление памятью. Если в низкоуровневых задачах (для которых разработан Си) это оправданно, то в прикладных задачах широкого спектра (на которые претендует C++) это не только является напрасным, но и чревато ошибками. Для C++ существуют библиотечные средства автоматического управления памятью, но они применяются далеко не всегда.

В то же время существует эмпирическое исследование[40], показывающее, что разница в скорости разработки программного обеспечения на C++ и на Java, в которой реализована автоматическая сборка мусора, невелика.

Качество и культура программирования[править | править код]

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

Поскольку все элементы семантики C++, отсутствующие в Си, были заимствованы из других языков (зачастую чужеродных Алголу), очевидно, что для адекватного применения этих взаимно противоречивых возможностей и избегания провоцируемых ими ошибок программисту на C++ крайне желательно знать непосредственно эти языки-источники и лежащую в их основе формальную базу. То есть чем более широк профессиональный кругозор программиста, тем лучше он сможет программировать на C++. Однако на практике положительная оценка и предпочтение использования C++ характерны преимущественно для программистов, не владеющих ни одним языком, не являющимся потомком Алгола (не считая ограниченного набора узкоспециализированных языков, таких как SQL, HTML, Perl и др.). Те же, кто имеет опыт использования языков, построенных на иной теоретической базе, чаще отзываются о C++ скептически и стараются его избегать, если нет вынужденности поддерживать унаследованный код. Это даёт основания полагать, что существует обратная зависимость между умением программировать на C++ в «хорошем» стиле и желанием использовать C++ в качестве инструмента разработки, из которой следует, что защищающие C++ программисты, по всей видимости, заблуждаются в отношении того, что именно следует считать «хорошим» стилем программирования.

В частности, Линус Торвальдс говорит, что использует мнение кандидатов о C++ в качестве критерия отсева (предпочитающие язык C++ языку Си отвергаются)[мнения 3]:

C++ — кошмарный язык. Его делает ещё более кошмарным тот факт, что множество недостаточно грамотных программистов используют его… Откровенно говоря, даже если нет никаких причин для выбора Си, кроме того чтобы держать C++-программистов подальше — то одно это уже будет достаточно веским основанием для использования Си.
…Я пришёл к выводу, что действительно предпочту выгнать любого, кто предпочтёт вести разработку проекта на C++, нежели на Си, чтобы этот человек не загубил проект, в который я вовлечён.

Исправление исправного[править | править код]

Непрерывная эволюция языка побуждает (а порой вынуждает) программистов раз за разом изменять уже отлаженный код — это не только удорожает разработку, но и несёт риск внедрения в отлаженный код новых ошибок. В частности, хотя изначально обратная совместимость с Си была одним из базовых принципов C++, с 1999 года Си перестал быть подмножеством C++, так что отлаженный код на Си уже не может использоваться в проекте на C++ без изменений.

Сложность ради самой сложности[править | править код]

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

Программисты — это зачастую яркие люди, которые гордятся … своей способностью справляться со сложностями и ловко обращаться с абстракциями. Часто они состязаются друг с другом, пытаясь выяснить, кто может создать «самые замысловатые и красивые сложности». … соперники полагают, что должны соревноваться с чужими «украшательствами» путём добавления собственных. Довольно скоро «массивная опухоль» становится индустриальным стандартом, и все используют большие, переполненные ошибками программы, которые не способны удовлетворить даже их создателей.

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

Саботаж[править | править код]

Отмечены случаи, когда нерадивые программисты, пользуясь сильной контекстной зависимостью C++ и отсутствием возможности отслеживания макроопределений компилятором тормозили разработку проекта, написав одну-две лишних, корректных с точки зрения компилятора, строки кода, но внедрив за их счёт труднообнаружимую спонтанно проявляющуюся ошибку. Например:

#define if(a) if(rand())


#define j i

В языках с доказанной корректностью, даже с развитыми макро-средствами, нанести урон подобным образом невозможно.

Ненадёжность продукта[править | править код]

Неоправданное обилие побочных эффектов (даже в таких простых операциях как индексация массива), в сочетании с отсутствием контроля со стороны системы времени исполнения языка и слабой системой типов, делает программы на C++ традиционно нестабильными, что исключает применение C++ при высоких требованиях отказоустойчивости. Кроме того, это увеличивает длительность самого процесса разработки[38]. Общеизвестные сообщения об ошибках при фатальных крахах прикладных программ, такие как «Access violation», «Pure virtual function call» или «Программа выполнила недопустимую операцию и будет закрыта», присущи в наибольшей степени именно C++. (Контр-примеры: для потомков ML фатальный крах в принципе невозможен — программа может навечно уйти в расчёты или выдать сообщение об ошибке, но никогда не обрушится — кроме того, контроль типов заметно сокращает время разработки[38]; семантика Форта обеспечивает практически гарантированное выявление ошибок на этапе разработки; Eiffel, Smalltalk и Erlang предоставляют простые способы обработки любых возможных ошибок самой программой без обрушения.)

Менеджмент проектов[править | править код]

Перечисленные выше факторы делают сложность менеджмента проектов на C++ одной из самых высоких в индустрии разработки ПО.

Джеймс Коггинс, в течение четырёх лет ведущий колонку в The C++ Report, дает такое объяснение:
 — Проблема в том, что программисты, работающие в ООП, экспериментировали с кровосмесительными приложениями и были нацелены на низкий уровень абстракции. Например, они строили такие классы как «связанный список», вместо «интерфейс пользователя», или «луч радиации», или «модель из конечных элементов». К несчастью, строгая проверка типов, которая помогает программистам C++ избегать ошибок, одновременно затрудняет построение больших объектов из маленьких.

Влияние и альтернативы[править | править код]

Неоднократно предпринимались попытки предложить альтернативы C++, как для прикладного, так и для низкоуровневого программирования (не обязательно заявляемые как «универсально применимые» языки).

Единственным прямым потомком C++ является язык D, задуманный как переработка C++ для устранения наиболее очевидных его проблем. Авторы отказались от совместимости с Си, сохранив синтаксис и многие базовые принципы C++ и введя в язык возможности, характерные для новых языков. В частности, в D нет препроцессора, включения заголовочных файлов, множественного наследования, но есть система модулей, интерфейсы, ассоциативные массивы, поддержка unicode в строках, сборка мусора (при сохранении возможности ручного управления памятью) встроенная многопоточность, вывод типов, явное объявление чистых функций и неизменяемых значений. Несмотря на преимущества, D не смог потеснить C++, его использование весьма ограничено: для программистов на C++ различия в возможностях (с учётом библиотек C++) не стали достаточно весомым стимулом для перехода, а для тех, кто нуждался в альтернативе C++, более привлекательными оказались более радикальные варианты.

Старейшим конкурентом C++ в задачах низкого уровня является Objective-C, также построенный по принципу объединения Си с объектной моделью (только объектная модель унаследована от Smalltalk), исходя из идеи, что эффективность и низкоуровневые возможности Си могут использоваться в сложно структурированных высокоуровневых задачах только посредством облачения их в ту или иную объектную модель.

Одной из первых альтернатив C++ в прикладном программировании стал язык Java, разработанный Sun Microsystems, который часто ошибочно считают прямым потомком C++. На деле в Java от C++ нет ничего, кроме базового синтаксиса — семантика Java унаследована от языка Модула-2, и основы семантики C++ в Java не прослеживаются. Учитывая это, а также генеалогию языков (Модула-2 является потомком Симулы, как и C++, но им не является Си), Java правильнее называть «троюродным племянником» C++, нежели «наследником». По тому же пути пошла компания Microsoft, предложив язык C#.

Попыткой совмещения безопасности и скорости разработки, характерных для Java и C#, с возможностями C++ явился диалект Managed C++, впоследствии доработанный и получивший название C++/CLI. Он разработан Microsoft в основном для переноса существующих проектов на C++ под платформу Microsoft.NET. Программы выполняются под управлением CLR и могут использовать весь массив библиотек .NET, но при этом накладывается ряд ограничений на использование возможностей C++, таких как множественное наследование, ручное управление памятью, прямое обращение к API операционной системы. Возможно, именно из-за ограничений, фактически сводящих C++ к C#, данный диалект не получил широкого признания.

Альтернативный путь развития языка Си — совмещение его не с объектно-ориентированным, а с аппликативным программированием, то есть улучшение абстракции, строгости и модульности низкоуровневых программ посредством обеспечения не инкапсуляции изменяемого состояния, а предсказуемости поведения и ссылочной прозрачности. Примерами работ в этом русле служат языки BitC, Cyclone и Limbo. Хотя есть и успешные попытки применения ФП в задачах реального времени без интеграции со средствами Си[42][43], всё же на данный момент (2013 г.) в низкоуровневой разработке применение в той или иной мере средств Си имеет лучшее соотношение трудоёмкости с результативностью. Много усилий было приложено разработчиками Python и Lua для обеспечения использования этих языков программистами на C++, так что из всех языков, достаточно тесно связанных с ФП, именно они чаще всего отмечаются в совместном использовании с C++ в одном проекте. Наиболее значимыми точками соприкосновения C++ с ФП можно считать привязки разработанных на C++ библиотек wxWidgets и Qt с характерной для C++ идеологией к языкам Lisp, Haskell и Python (в большинстве случаев привязки к функциональным языкам делают для библиотек, написанных на Си или на других функциональных языках).

Ещё одним языком, рассматриваемым как конкурент C++, стал Nemerle, являющийся результатом попытки совместить модель типизации Хиндли-Милнера и макро-подмножество Common Lisp с языком C#.[44]. В том же русле находится созданный Microsoft язык F# — диалект ML, адаптированный для среды .NET.

Попыткой создать промышленную замену C/C++ стал разработанный в корпорации Google в 2009 году язык программирования Go. Авторы языка прямо указывают, что мотивом для его создания были недостатки процесса разработки, вызванные особенностями языков Си и C++[45]. Go - компактный, несложный по структуре императивный язык с Си-подобным синтаксисом, без препроцессора, со статической типизацией, строгим контролем типов, системой пакетов, автоматическим управлением памятью, некоторыми функциональными чертами (функции-значения, замыкания), экономно построенной ООП-подсистемой без поддержки наследования реализации, но с интерфейсами и утиной типизацией, встроенной многопоточностью, основанной на сопрограммах и каналах (a-la Occam). Язык позиционируется как альтернатива C++, то есть, в первую очередь, средство групповой разработки высокоэффективных вычислительных систем большой сложности, в том числе распределённых, допускающее, при необходимости, низкоуровневое программирование.


См. также[править | править код]

Примечания[править | править код]

  1. Губина Г. Г. Компьютерный английский. Ч. I. Computer English. Part I. Учебное пособие. — С. 385.
  2. Bjarne Stroustrup's FAQ (англ.). Bjarne Stroustrup (Ocober 1, 2017). — «The name C++ (pronounced "see plus plus")». Проверено 4 декабря 2017.
  3. Шилдт, 1998.
  4. 1 2 Страуструп, 1999, 2.1. Что такое C++?, с. 57.
  5. Стэнли Липпман, Pure C++: Hello, C++/CLI (англ.)
  6. 1 2 Страуструп, 1999, 1.4. Исторические замечания, с. 46.
  7. C++ — Standards
  8. Bjarne Stroustrup. C++ Glossary. Проверено 8 июня 2007. Архивировано 27 мая 2012 года.
  9. Where do I find the current C or C++ standard documents? http://stackoverflow.com/questions/81656/where-do-i-find-the-current-c-or-c-standard-documents
  10. See a list at http://en.cppreference.com/w/cpp/experimental Visited 5 January 2015.
  11. Яндекс организует рабочую группу по стандартизации языка C++. ИКС Медиа.
  12. 1 2 Страуструп, Дизайн и эволюция C++, 2007.
  13. ISO/IEC 14882:1998, раздел 6.4, пункт 4: «The value of a condition that is an initialized declaration in a statement other than a 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».
  14. Страуструп, 1999, 1.6.
  15. std::vector — cppreference.com
  16. std::realloc — cppreference.com
  17. STLport: Welcome!
  18. Интервью Б. Страуструпа LinuxWorld.com
  19. Интервью Б. Страуструпа журналу «Системный администратор»
  20. CNews: Эксклюзивное интервью с создателем языка программирования C++
  21. 1 2 Alan Kay's Definition Of Object Oriented Programming. Архивировано 13 августа 2013 года.
  22. 1 2 3 Martin Ward. Language Oriented Programming[en]. — Computer Science Department, Science Labs, 1994.
  23. Paul Hudak. Modular Domain Specific Languages and Tools. — Department of Computer Science, Yale University.
  24. Don Clugston, CSG Solar Pty Ltd (Перевод: Денис Буличенко). Указатели на функции-члены и реализация самых быстрых делегатов на C++. — RSDN Magazine #6-2004.
  25. Шилдт, Теория и практика C++, 1996, с. 64—67.
  26. 1 2 3 Ian Joyner. A Critique of C++ and Programming and Language Trends of the 1990s - 3rd Edition. — копирайт и список изданий.
  27. Paulson, Lawrence C. ML for the Working Programmer, 2nd edition. — University of Cambridge: Cambridge University Press, 1996. — ISBN 0-521-57050-6 (hardback), 0-521-56543-X (paperback)., c.271-285
  28. 1 2 3 4 Открытая переписка gmane.comp.version-control.git от 06.09.2007. Архивировано 13 августа 2013 года.
  29. Андрей Карпов. 20 ловушек переноса Си++ - кода на 64-битную платформу. — RSDN Magazine #1-2007, 25.04.2007.
  30. Walid Taha. Domain-Specific Languages. — Department of Computer Science, Rice University.
  31. Lugovsky V.S. Using a hierarchy of Domain Specific Languages in complex software systems design. — 2008.
  32. Страуструп, Программирование: принципы и практика использования C++, 2001
  33. Dave Gottner. Templates Without Code Bloat. — Dr. Dobb's Journal, январь 1995.
  34. Adrian Stone. Minimizing Code Bloat: Redundant Template Instantiation. Game Angst (22 сентября 2009). Проверено 19 января 2010. Архивировано 27 мая 2012 года.
  35. Herb Sutter. C++ Conformance Roundup. — Dr. Dobb's Journal, январь 2001.
  36. Are there any compilers that implement all of this?. comp.std.c++ frequently asked questions / The C++ language. Comeau Computing (англ.) (10 декабря 2008). Проверено 19 января 2010. Архивировано 27 мая 2012 года.
  37. Scott Meyers. Code Bloat due to Templates. comp.lang.c++.moderated. Usenet (16 мая 2002). Проверено 19 января 2010.
  38. 1 2 3 Ray Tracer Language Comparison (бенчмарк языков программирования — ffconsultancy.com/languages/ray_tracer/)
  39. Boehm H. Advantages and Disadvantages of Conservative Garbage Collection.
    (ссылка изРеймонд, Эрик. Искусство программирования для Unix.. — 2005. — С. 357. — 544 с. — ISBN 5-8459-0791-8.)
  40. Lutz Prechelt. An empirical comparison of C, C++, Java, Perl, Python, Rexx, and Tcl (англ.). Технологический институт Карлсруэ. Проверено 26 октября 2013.
  41. Эрик Реймонд. Искусство программирования для Unix = The Art of Unix. — Вильямс, 2005. — ISBN 5-8459-0791-8.
  42. Zhanyong Wan, Walid Taha. Архивировано 13 августа 2013 года., and Paul Hudak. Event-Driven FRP // Department of Computer Science, Yale University.
  43. MLKit. Архивировано 31 июля 2013 года.
  44. Чистяков Влад aka VladD2. Синтаксический сахар или C++ vs. Nemerle :) // RSDN Magazine #1-2006. — 24.05.2006.
  45. Go at Google: Language Design in the Service of Software Engineering. talks.golang.org. Проверено 19 сентября 2017.
Мнения
  1. Programming Language Popularity (2009). Проверено 16 января 2009. Архивировано 27 мая 2012 года.
  2. TIOBE Programming Community Index (2009). Проверено 6 мая 2009. Архивировано 27 мая 2012 года.
  3. 1 2 Открытая переписка gmane.comp.version-control.git от 06.09.2007. Архивировано 13 августа 2013 года.
  4. vanDooren. C++ keyword of the day: export. Blogs@MSMVPs (24 сентября 2008). — «The export keyword is a bit like the Higgs boson of C++. Theoretically it exists, it is described by the standard, and noone has seen it in the wild. … There is 1 C++ compiler front-end in the world which actually supports it». Проверено 19 января 2010. Архивировано 27 мая 2012 года.
Пояснения
  1. Очевидно, что если сложность системы или элемента системы превышает порог возможностей человека по осознанию, то система или элемент системы не будут до конца осознаны. При этом трансляция высокоуровневых конструкций (даже всего лишь арифметических выражений) сама по себе является очень сложной задачей: она требует применения динамического программирования и/или большого числа эвристик; даже для эффективного выделения регистров под временные переменные в данном контексте требуется решить задачу раскраски ненаправленного ациклического графа в минимальное число цветов. Таким образом, необходимость решать задачу эффективной трансляции одновременно с полноценной и адекватной реализацией требуемого функционала повышает общую сложность разработки, по меньшей мере, по экспоненциальному закону относительно сложности реализации того же функционала на предметно-специфичном ЯСВУ, и делает само программирование NP-полным. Учитывая ограниченность способностей человека по восприятию и переработке информации — фактически невозможным, начиная с некоторого (вполне обозримого) порога сложности. Однако, методы трансляции хорошо формализуемы и могут быть воплощены в виде компилятора языка достаточно высокого уровня, обеспечивающего максимальную понимаемость кода, а использование такого языка человеком решает проблему сложности (см. M.Ward, Language Oriented Programming). Таким образом, для любого достаточно сложного алгоритма достаточно развитый компилятор произведёт заведомо более эффективный код, чем человек на языке низкого уровня; и для любых достаточно сложных условий всегда разумнее потратить часть средств на разработку компилятора высокоуровневого предметно-специфичного языка, чем писать на низкоуровневом языке общего назначения. Всё это хорошо известно в информатике (и именно этим обусловлено появление Фортрана, а следом и языков более высокого уровня), но подвергается сомнению сторонниками императивной парадигмы программирования из-за чрезвычайно высокой сложности реализации оптимизирующих трансляторов императивными средствами и заведомой ограниченности возможностей оптимизации императивных языков. В функциональном программировании парсеры реализуются тривиально, а возможностей оптимизации существенно больше, поэтому языково-ориентированное проектирование сложных систем весьма распространено, и вынесение функциональности в код на Си для явного контроля за эффективностью осуществляется лишь при крайней необходимости.
  2. Например, Boost.Lambda позиционируется как лямбда-функция, однако C++ не воплощает лямбда-исчисление Чёрча целиком; имитация комбинаторов посредством языка шаблонов слишком затруднено, чтобы ожидать их использование на практике; продолжения, унаследованные от Си, считаются неидеоматичными и опасными, а их реализация посредством языка шаблонов невозможна; кроме того, применение λ-подъёма[en] для оптимизации C++ невозможно,— так что фактчески Boost.Lambda — это просто анонимная функция, а не объект лямбда-исчисления.

Литература[править | править код]

Ссылки[править | править код]