Программирование >>  Синтаксис инициирования исключений 

1 2 [ 3 ] 4 5 6 ... 82


Константный указатель

С константными указателями все наоборот: адрес изменять нельзя, но зато можно изменять содержимое памяти по этому адресу.

int i = 17; int j = 29;

int* const p; Нельзя! Должно быть задано начальное значение

int* const p1 = &i; Порядок

*p1 = 29; Можно; величина, на которую ссылается указатель,

может изменяться

p1 = &j; Нельзя

Константный указатель на константу

Константный указатель на константу (попробуйте-ка трижды быстро произнести это вслух!) изменить вообще нельзя. Это неизменяемый адрес неизменяемой величины.

int i = 17;

int j = 29;

const int* const

Нельзя.

const int* const

p1 = &i;

Можно

*p1 = 29;

Нельзя

p1 = &j;

Нельзя

Константные аргументы функций

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

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); Порядок



Неконстантные аргументы функций

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

void f(int*); int i = 17; const int* p = &i; const int j = 29;

f(&i); Можно, потому что i - не константа

f(p); Нельзя

f(&j); Тоже нельзя, потому что j - константа

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

Константные функции классов

В константных функциях классов переменная this интерпретируется как указатель на константу. Компилятор даст вам по рукам, если вы попробуете воспользоваться переменной this для изменения переменной класса или найти для нее иное, неконстантное применение. Смысл ключевого слова const зависит от его места в объявлении функции; для константных функций оно, словно бородавка, торчит после сигнатуры функции.

class foo { private:

int x; public:

void f() const;

void g();

void

h(int*);

void

m(foo*);

void

foo::f();

x = 17;

this->g();

h(&x);

m(this);

Нельзя: изменяется переменная класса

Нельзя: g - некоторая функция

Нельзя: h может изменить x

Нельзя: неконстантный аргумент в m()

Первая ошибка - попытка изменить переменную класса через this. В константных функциях класса foo переменная this фактически объявляется как const foo* this; . Вторая ошибка сложнее. Из приведенного фрагмента неизвестно, изменяет ли функция g какие-либо переменные класса foo, но это и не важно; одной возможности достаточно, чтобы ваш компилятор разразился негодующими воплями. Из константной функции класса нельзя вызывать неконстантные функции через this. Похожая ситуация возникает с третьей и четвертой ошибкой - компилятор попытается спасти вас от самого себя и не допустит потенциально опасные строки.

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



нарушает ваш пуританский стиль кодирования. Мораль: константные функции классов нужно использовать либо с полным фанатизмом (желательно), либо не использовать вовсе.

Стековые и динамические объекты

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

int x = 17;

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

Вы должны понимать эту теоретическую особенность C++, чтобы правильно относиться к стековым и динамическим объектам и связанным с ними переменным.

Размещение в стеке

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

int i;

foo f(constructor args);

Перед выходом из блока вызываются деструкторы i и f

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

int i;

foo f;

SomeFunction(&f);

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

Динамическое размещение

Чтобы выделить память для объекта в куче (heap), воспользуйтесь оператором new. foo* f = new foo(constructor args);

Вроде бы все просто. Оператор new выделяет память и вызывает соответствующий конструктор на основании переданных аргументов. Но когда этот объект уничтожается? Подробный ответ на этот вопрос займет примерно треть книги, но я не буду вдаваться в технические детали и отвечу так: Когда



1 2 [ 3 ] 4 5 6 ... 82

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика