Анимация
JavaScript
|
Главная Библионтека Синтаксис С++ За годы преподавания C++ я узнал, что подавляющее большинство программистов C++ (включая самых опытных) редко пользуется некоторыми возможностями языка. Конечно, дело это сугубо индивидуальное, но при всей сложности и глубине C++ небольшой обзор не повредит никому. В этой и двух следующих главах я постараюсь выровнять уровень подготовки читателей перед тем, как переходить к действительно интересным темам. Эта глава не заменит Annotated Reference Manual или другое справочное руководство - вы не найдете в ней полной спецификации языка. Я лишь рассмотрю некоторые языковые средства, которые часто понимаются неверно или не понимаются вовсе. Придержите шляпу и приготовьтесь к стремительному облету синтаксиса C++! Переменные и константы Болтовню о том, что такое переменные и для чего они нужны, пропускаем. Нашего внимания заслуживают две темы: константность и сравнение динамических объектов со стековыми. const Ключевое слово const, которое в разных контекстах принимает разные значения, одно из самых информативных в C++. Да, между этими значениями есть кое-что общее, но вам все равно придется запомнить все конкретные случаи. Константные переменные Если переменная объявлена с ключевым словом const, значит, она не должна меняться. После определения константной переменной вы уже не сможете изменить ее значение или передать ее в качестве аргумента функции, которая не гарантирует ее неизменности. Рассмотрим простой пример с константной целой переменной. const int j = 17; Целая константа j = 29; Нельзя, значение не должно меняться const int i; Нельзя, отсутствует начальное значение Третья строка неверна, поскольку в ней компилятору предлагается определить случайную переменную, которую никогда не удастся изменить, - этакий странный генератор случайных целых констант. Вообще говоря, вы сообщаете компилятору, какой конструктор он должен использовать в конкретном случае. Если бы переменная i относилась к нетривиальному классу, то при объявлении константного экземпляра пришлось бы явно указать конструктор и его аргументы. int - вырожденный случай, поскольку на самом деле const int j=17; - то же, что и int j(17). Но вот компилятор узнал, что нечто должно быть константным. Он просыпается и начинает искать ошибки - не только фактические, но и потенциальные. Компилятор не разрешит использовать ваше константное нечто в любом неконстантном контексте, даже если шестилетний ребенок разберется в программе и докажет, что в ней нет ни одной ошибки. const i = 17; int& j = 1; Нельзя, потому что позднее j может измениться Не важно, будете ли вы изменять величину, на которую ссылается j. Компилятор предполагает, что вам захочется это сделать, и на всякий случай устраняет искушение. Иначе говоря, константность - свойство переменной, а не данных, поэтому неконстантная переменная не может ссылаться на константную величину. const и #define Две следующие строки не эквивалентны: const int i = 17; #define i 17; В первой строке определяется переменная, занимающая некоторую область памяти, а во второй - макрос. Обычно отличия несущественны, если не считать одного-двух лишних тактов, затраченных на каждое обращение к константной переменной. Однако если переменная является глобальной и принадлежит нетривиальному классу со своим конструктором, ситуация резко меняется. Дополнительные сведения приведены в разделе «Инициализация глобальных объектов» этой главы. Константы в перечислениях Перечисления (епит) не очень широко использовались в языке С по одной простой причине: символические имена констант имеют глобальную область действия и быстро захламляют пространство имен. В C++ эта проблема исчезла, поскольку область действия символических имен ограничивается классом или структурой. class Foo { public: enum Status { kOpen = 1, kClosed }; Где-то в программе Foo::Status s = Foo::kOpen; Обратите внимание - область действия должна быть явно указана как в имени типа, так и в символическом имени. Следовательно, символические имена kOpen и kClosed можно использовать в программе и для других целей. Компилятор рассматривает символические имена перечислений как макросы, а не как константные переменные. Это обстоятельство может оказаться важным при инициализации глобальных переменных (см, далее в этой главе). Указатель на константу С указателями дело обстоит несколько сложнее, поскольку приходится учитывать два значения: адрес и содержимое памяти по этому адресу. В следующем примере р - это указатель на константу; находящийся в указателе адрес может измениться, но содержимое памяти по этому адресу - нет. const int* p; int i = 17; p = &i; Можно *p = 29; Нельзя Сказанное также относится к структурам и объектам. class foo { public: int x; const foo* f = new foo; f->x = 17; Нельзя, присвоение членам класса не допускается Константный указатель С константными указателями все наоборот: адрес изменять нельзя, но зато можно изменять содержимое памяти по этому адресу. int i = 17; int j = 29; int* const p; Нельзя! Должно быть задано начальное значение int* const p1 = &i; Порядок *p1 = 29; Можно; величина, на которую ссылается указатель, может изменяться p1 = &j; Нельзя Константный указатель на константу Константный указатель на константу (попробуйте-ка трижды быстро произнести это вслух!) изменить вообще нельзя. Это неизменяемый адрес неизменяемой величины. int i = 17;
Константные аргументы функций Константный аргумент функции должен подчиняться тем же правилам, что и любая другая константная переменная. void f(const int* p) *p = 17; Нельзя int i = 29; p = &i; Можно, но зачем? Где-то в программе int i = 17; f(&i); Порядок, фактический аргумент не обязан быть константой Обратите внимание - аргумент, указанный при вызове функции, не обязан быть константным. Этот вопрос целиком остается на усмотрение стороны-получателя. Передача по ссылке осуществляется по тем же правилам, что и передача по адресу. void f(const int& p) p = 17; Нельзя int i = 29; p = i; Можно (на грани фола) Где-то глубоко в программе int i = 17; f(i); Порядок 0 1 [ 2 ] 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |