C++ учебник
Начало работы с C ++
Поиск…
замечания
Программа «Hello World» является распространенным примером, который можно просто использовать для проверки наличия компилятора и библиотеки. Он использует стандартную библиотеку C ++ с std::cout
из <iostream>
и имеет только один файл для компиляции, что минимизирует вероятность возможной ошибки пользователя во время компиляции.
Процесс компиляции C ++-программы по своей сути отличается между компиляторами и операционными системами. В разделе « Компиляция и построение» содержатся сведения о том, как скомпилировать код C ++ на разных платформах для различных компиляторов.
Версии
Версия | стандарт | Дата выхода |
---|---|---|
С ++ 98 | ISO / IEC 14882: 1998 | 1998-09-01 |
C ++ 03 | ISO / IEC 14882: 2003 | 2003-10-16 |
C ++ 11 | ISO / IEC 14882: 2011 | 2011-09-01 |
C ++ 14 | ISO / IEC 14882: 2014 | 2014-12-15 |
C ++ 17 | TBD | 2017-01-01 |
C ++ 20 | TBD | 2020-01-01 |
Привет, мир
Эта программа печатает Hello World!
к стандартному потоку вывода:
#include <iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
}
Смотрите на живую в Колиру .
Анализ
Давайте подробно рассмотрим каждую часть этого кода:
#include <iostream>
- это препроцессорная директива, которая включает в себя содержимое стандартного файлаiostream
заголовочного файла C ++.iostream
- это стандартный файл заголовка библиотеки, который содержит определения стандартных потоков ввода и вывода. Эти определения включены в пространство именstd
, как описано ниже.Стандартные потоки ввода / вывода (I / O) обеспечивают способы ввода и вывода программ во внешнюю систему - обычно терминал.
int main() { ... }
определяет новую функцию с именемmain
. По соглашениюmain
функция называется исполнением программы. В программе на C ++ должна быть только однаmain
функция, и она должна всегда возвращать число типаint
.Здесь
int
- это то, что называется возвращаемым типом функции . Значение, возвращаемоеmain
функцией, - это код выхода.По соглашению код выхода программы
0
илиEXIT_SUCCESS
интерпретируется как успех системой, выполняющей программу. Любой другой код возврата связан с ошибкой.Если оператор
return
не присутствует,main
функция (и, следовательно, сама программа) возвращает0
по умолчанию. В этом примере нам не нужно явно писатьreturn 0;
,Все остальные функции, кроме тех, которые возвращают тип
void
, должны явно возвращать значение в соответствии с их типом возвращаемого значения или иначе не должны возвращаться вообще.
std::cout << "Hello World!" << std::endl;
печатает «Hello World!» к стандартному потоку вывода:std
- пространство имен , а::
- оператор разрешения области, который позволяет искать объекты по имени в пространстве имен.Существует много пространств имен. Здесь мы используем
::
чтобы показать, что мы хотим использоватьcout
из пространства именstd
. Для получения дополнительной информации см. Оператор разрешения области действия - Документация Microsoft .std::cout
- это стандартный объект потока вывода , определенный вiostream
, и он выводит на стандартный вывод (stdout
).<<
является в этом контексте оператором вставки потока , так называемым, потому что он вставляет объект в объект потока .Стандартная библиотека определяет оператора
<<
для выполнения ввода данных для определенных типов данных в выходные потоки.stream << content
вставляетcontent
в поток и возвращает тот же, но обновленный поток. Это позволяет привязывать потоковые вставки:std::cout << "Foo" << " Bar";
печатает «FooBar» на консоли."Hello World!"
является символьным строковым литералом или «текстовым литералом». Оператор вставки потока для символьных строковых литералов определяется в файлеiostream
.std::endl
- специальный объект манипулятора ввода-вывода , также определенный в файлеiostream
. Вставка манипулятора в поток изменяет состояние потока.Манипулятор потока
std::endl
выполняет две функции: сначала он вставляет символ конца строки, а затем очищает буфер потока, чтобы текст отображался на консоли. Это гарантирует, что данные, вставленные в поток, действительно отображаются на консоли. (Данные о потоке обычно хранятся в буфере, а затем «размываются» партиями, если вы немедленно не отключите флеш.)Альтернативный метод, который позволяет избежать флеша, заключается в следующем:
std::cout << "Hello World!\n";
где
\n
- последовательность escape символов для символа новой строки.Точка с запятой (
;
) уведомляет компилятор о завершении оператора. Все инструкции C ++ и определения классов требуют конечной / конечной точки с запятой.
Комментарии
Комментарий - это способ помещать произвольный текст внутри исходного кода, не компилятор C ++ интерпретирует его с любым функциональным значением. Комментарии используются, чтобы дать представление о дизайне или методе программы.
В C ++ есть два типа комментариев:
Однострочные комментарии
Двойная последовательность косой черты //
будет отмечать весь текст до появления новой строки в виде комментария:
int main()
{
// This is a single-line comment.
int a; // this also is a single-line comment
int i; // this is another single-line comment
}
Комментарии к C-Style / Block
Последовательность /*
используется для объявления начала блока комментариев, а последовательность */
используется для объявления конца комментария. Весь текст между начальной и конечной последовательностями интерпретируется как комментарий, даже если текст в остальном является синтаксисом C ++. Они иногда называются комментариями «C-style», так как этот синтаксис комментария унаследован от языка предшественника C ++, C:
int main()
{
/*
* This is a block comment.
*/
int a;
}
В любом комментарии блока вы можете написать все, что хотите. Когда компилятор встречает символ */
, он завершает комментарий блока:
int main()
{
/* A block comment with the symbol /*
Note that the compiler is not affected by the second /*
however, once the end-block-comment symbol is reached,
the comment ends.
*/
int a;
}
Вышеприведенный пример является допустимым кодом C ++ (и C). Однако наличие дополнительного /*
внутри комментария блока может привести к предупреждению некоторых компиляторов.
Блочные комментарии также могут начинаться и заканчиваться в одной строке. Например:
void SomeFunction(/* argument 1 */ int a, /* argument 2 */ int b);
Важность комментариев
Как и во всех языках программирования, комментарии предоставляют несколько преимуществ:
- Явная документация кода, упрощающая чтение / обслуживание
- Объяснение цели и функциональности кода
- Подробная информация об истории или аргументах кода
- Размещение авторских прав / лицензий, примечаний к проектам, особая благодарность, кредиты вкладчиков и т. Д. Непосредственно в исходном коде.
Однако комментарии также имеют свои недостатки:
- Они должны поддерживаться, чтобы отражать любые изменения в коде
- Чрезмерные комментарии, как правило, делают код менее удобочитаемым
Потребность в комментариях может быть уменьшена путем написания четкого, самодокументирующего кода. Простым примером является использование пояснительных имен для переменных, функций и типов. Факторизация логически связанных задач в дискретные функции идет рука об руку с этим.
Маркеры комментариев, используемые для отключения кода
Во время разработки комментарии также могут использоваться для быстрого отключения частей кода без его удаления. Это часто полезно для тестирования или отладки, но не является хорошим стилем для чего угодно, кроме временных изменений. Это часто называют «комментированием».
Точно так же сохранение старых версий части кода в комментарии для справочных целей неодобрительно, поскольку оно загромождает файлы, при этом мало ценное по сравнению с исследованием истории кода через систему управления версиями.
функция
Функция - это единица кода, представляющая последовательность операторов.
Функции могут принимать аргументы или значения и возвращать одно значение (или нет). Для использования функции для значений аргументов используется вызов функции, а использование самого вызова функции заменяется его возвращаемым значением.
Каждая функция имеет сигнатуру типа - типы ее аргументов и тип возвращаемого типа.
Функции вдохновлены понятиями процедуры и математической функции.
- Примечание. Функции C ++ являются по существу процедурами и не соответствуют точному определению или правилам математических функций.
Функции часто предназначены для выполнения конкретной задачи. и может быть вызван из других частей программы. Функция должна быть объявлена и определена до ее вызова в другом месте программы.
- Примечание: популярные определения функций могут быть скрыты в других включенных файлах (часто для удобства и повторного использования во многих файлах). Это обычное использование файлов заголовков.
Объявление функции
Объявление функции объявляет о существовании функции с ее именем и сигнатурой типа компилятору. Синтаксис следующий:
int add2(int i); // The function is of the type (int) -> (int)
В приведенном выше примере int add2(int i)
объявляет компилятору следующее:
- Тип возврата -
int
. - Имя функции -
add2
. - Число аргументов функции равно 1:
- Первый аргумент имеет тип
int
. - Первый аргумент будет обозначен в содержимом функции именем
i
.
- Первый аргумент имеет тип
Имя аргумента необязательно; декларация для функции также может быть следующей:
int add2(int); // Omitting the function arguments' name is also permitted.
В соответствии с одним правилом определения функция с определенной сигнатурой типа может быть объявлена или определена только один раз во всей кодовой базе C ++, видимой компилятору C ++. Другими словами, функции с определенной сигнатурой типа не могут быть переопределены - их нужно определять только один раз. Таким образом, недопустимо следующее: C ++:
int add2(int i); // The compiler will note that add2 is a function (int) -> int
int add2(int j); // As add2 already has a definition of (int) -> int, the compiler
// will regard this as an error.
Если функция ничего не возвращает, ее возвращаемый тип записывается как void
. Если он не принимает никаких параметров, список параметров должен быть пустым.
void do_something(); // The function takes no parameters, and does not return anything.
// Note that it can still affect variables it has access to.
Вызов функции
Функция может быть вызвана после ее объявления. Например, следующая программа вызывает add2
со значением 2
в функции main
:
#include <iostream>
int add2(int i); // Declaration of add2
// Note: add2 is still missing a DEFINITION.
// Even though it doesn't appear directly in code,
// add2's definition may be LINKED in from another object file.
int main()
{
std::cout << add2(2) << "\n"; // add2(2) will be evaluated at this point,
// and the result is printed.
return 0;
}
Здесь add2(2)
является синтаксисом вызова функции.
Определение функции
Определение функции * похоже на объявление, за исключением того, что оно также содержит код, который выполняется, когда функция вызывается внутри ее тела.
Примером определения функции для add2
может быть:
int add2(int i) // Data that is passed into (int i) will be referred to by the name i
{ // while in the function's curly brackets or "scope."
int j = i + 2; // Definition of a variable j as the value of i+2.
return j; // Returning or, in essence, substitution of j for a function call to
// add2.
}
Перегрузка функции
Вы можете создавать несколько функций с тем же именем, но с разными параметрами.
int add2(int i) // Code contained in this definition will be evaluated
{ // when add2() is called with one parameter.
int j = i + 2;
return j;
}
int add2(int i, int j) // However, when add2() is called with two parameters, the
{ // code from the initial declaration will be overloaded,
int k = i + j + 2 ; // and the code in this declaration will be evaluated
return k; // instead.
}
Обе функции вызываются одним и тем же именем add2
, но действительная функция, которая вызывается, напрямую зависит от количества и типа параметров в вызове. В большинстве случаев компилятор C ++ может вычислить, какую функцию вызывать. В некоторых случаях тип должен быть явно указан.
Параметры по умолчанию
Значения по умолчанию для параметров функции могут быть указаны только в объявлениях функций.
int multiply(int a, int b = 7); // b has default value of 7.
int multiply(int a, int b)
{
return a * b; // If multiply() is called with one parameter, the
} // value will be multiplied by the default, 7.
В этом примере multiply()
можно вызвать с одним или двумя параметрами. Если задан только один параметр, b
будет иметь значение по умолчанию 7. По умолчанию аргументы должны быть помещены в последние аргументы функции. Например:
int multiply(int a = 10, int b = 20); // This is legal
int multiply(int a = 10, int b); // This is illegal since int a is in the former
Специальные вызовы функций - Операторы
Существуют специальные вызовы функций в C ++, которые имеют различный синтаксис, чем name_of_function(value1, value2, value3)
. Наиболее распространенным примером является оператор.
Некоторые специальные последовательности символов, которые будут сведены к вызову функции компилятором, например !
, +
, -
, *
, %
, и <<
и многое другое. Эти специальные символы обычно связаны с использованием не-программирования или используются для эстетики (например, символ +
обычно распознается как символ добавления как в программировании на С ++, так и в элементарной математике).
C ++ обрабатывает эти последовательности символов специальным синтаксисом; но, по существу, каждое возникновение оператора сводится к вызову функции. Например, следующее выражение C ++:
3+3
эквивалентно следующему вызову функции:
operator+(3, 3)
Все имена функций оператора начинаются с operator
.
В то время как в непосредственном предшественнике C ++ имена операторов C не могут присваиваться различным значениям, предоставляя дополнительные определения с сигнатурами разных типов, в C ++ это действительно. «Скрытие» дополнительных определений функций под одним уникальным именем функции называется перегрузкой оператора на C ++ и является относительно распространенным, но не универсальным соглашением на C ++.
Видимость прототипов функций и деклараций
В C ++ код должен быть объявлен или определен перед использованием. Например, следующее создает ошибку времени компиляции:
int main()
{
foo(2); // error: foo is called, but has not yet been declared
}
void foo(int x) // this later definition is not known in main
{
}
Это можно решить двумя способами: поставить определение или декларацию foo()
перед его использованием в main()
. Вот один пример:
void foo(int x) {} //Declare the foo function and body first
int main()
{
foo(2); // OK: foo is completely defined beforehand, so it can be called here.
}
Однако также возможно «форвард-объявить» функцию, помещая только объявление «прототипа» перед ее использованием и затем определяя тело функции позже:
void foo(int); // Prototype declaration of foo, seen by main
// Must specify return type, name, and argument list types
int main()
{
foo(2); // OK: foo is known, called even though its body is not yet defined
}
void foo(int x) //Must match the prototype
{
// Define body of foo here
}
Прототип должен указывать тип возвращаемого значения ( void
), имя функции ( foo
) и типы переменных списка аргументов ( int
), но имена аргументов НЕ требуются .
Одним из распространенных способов интегрирования этого в организацию исходных файлов является создание файла заголовка, содержащего все объявления прототипов:
// foo.h
void foo(int); // prototype declaration
а затем предоставить полное определение в другом месте:
// foo.cpp --> foo.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
void foo(int x) { } // foo's body definition
а затем, после компиляции, связать соответствующий файл объекта foo.o
в скомпилированном объектном файле, где он используется в фазе связывания, main.o
:
// main.cpp --> main.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
int main() { foo(2); } // foo is valid to call because its prototype declaration was beforehand.
// the prototype and body definitions of foo are linked through the object files
Ошибка «неразрешенного внешнего символа» возникает, когда прототип функции и вызов существуют, но тело функции не определено. Это может быть сложнее решить, поскольку компилятор не сообщит об ошибке до этапа окончательной компоновки, и он не знает, в какую строку перейти в код, чтобы показать ошибку.
Стандартный процесс компиляции C ++
Исполняемый программный код C ++ обычно создается компилятором.
Компилятор - это программа, которая переводит код с языка программирования в другую форму, которая (более) выполняется непосредственно для компьютера. Использование компилятора для перевода кода называется компиляцией.
C ++ наследует форму своего процесса компиляции с его «родительского» языка, C. Ниже приведен список четырех основных шагов компиляции в C ++:
- Препроцессор C ++ копирует содержимое любых включенных заголовочных файлов в файл исходного кода, генерирует макрокод и заменяет символические константы, определенные с помощью #define, с их значениями.
- Расширенный файл исходного кода, созданный препроцессором C ++, скомпилирован в ассемблере, подходящем для платформы.
- Код ассемблера, сгенерированный компилятором, собран в соответствующий объектный код для платформы.
- Файл объектного кода, сгенерированный ассемблером, связан вместе с файлами объектных кодов для любых библиотечных функций, используемых для создания исполняемого файла.
- Примечание: некоторые скомпилированные коды связаны друг с другом, но не для создания окончательной программы. Обычно этот «связанный» код также может быть упакован в формат, который может использоваться другими программами. Этот «пакет упакованного, полезного кода» - это то, что программисты на C ++ называют библиотекой.
Многие компиляторы C ++ могут также объединять или объединять определенные части процесса компиляции для удобства или для дополнительного анализа. Многие программисты на С ++ будут использовать разные инструменты, но все инструменты, как правило, следуют этому обобщенному процессу, когда они участвуют в создании программы.
Ссылка ниже расширяет эту дискуссию и обеспечивает приятную графику. [1]: http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html
препроцессор
Препроцессор является важной частью компилятора.
Он редактирует исходный код, вырезает несколько битов, меняет другие и добавляет другие вещи.
В исходных файлах мы можем включать директивы препроцессора. Эти директивы говорят препроцессору выполнять определенные действия. Директива начинается с # на новой строке. Пример:
#define ZERO 0
Первая препроцессорная директива, которую вы встретите, вероятно, является
#include <something>
директивы. Что он делает, так это все something
и вставляет в файл, где была указатель. Всемирная программа приветствия начинается с линии
#include <iostream>
Эта строка добавляет функции и объекты, которые позволяют использовать стандартный ввод и вывод.
Язык C, который также использует препроцессор, не имеет столько заголовочных файлов, как язык C ++, но на C ++ вы можете использовать все файлы заголовков C.
Следующей важной директивой, вероятно, является
#define something something_else
директивы. Это говорит препроцессору, что, когда он идет по файлу, он должен заменить каждое появление something
something_else
. Это также может сделать вещи похожими на функции, но это, вероятно, считается продвинутым C ++.
something_else
не требуется, но если вы определите something
как ничто, то внешние директивы препроцессора, все вхождения something
то исчезнут.
Это действительно полезно из-за #if
, #else
и #ifdef
. Формат для них будет следующим:
#if something==true
//code
#else
//more code
#endif
#ifdef thing_that_you_want_to_know_if_is_defined
//code
#endif
Эти директивы вставляют код, который находится в истинном бите, и удаляют ложные биты. это может быть использовано для того, чтобы иметь биты кода, которые включены только в определенные операционные системы, без необходимости переписывать весь код.