А.В. Столяров - Введение в язык Си++ (1114949), страница 7
Текст из файла (страница 7)
п.),а затем либо использовать значение найденной переменной, либо выполнить над ней то или иное присваивание. В такой ситуации поиск переменной можно выделить в отдельную функцию, возвращающую ссылкуна найденную переменную. Если такая функция имеет профильin t & fin d _ v ar(/*p aram s*/);то допустимы будут, например, такие операторы:in t х = f i n d _ v a r ( / * . .
. * / ) + 5;f i n d _ v a r ( / * . . . * / ) = 3;f i n d _ v a r ( / * . . . * / ) *= 10;find_ v ar(/*.. .*/)++;int у = ++find_var( / * . . . * / ) ;§2.7. М о ди ф и к ато р constЧитателю, возможно, уже знакомо ключевое слово const, которое изначально появилось в С и + + , но с недавних пор вошло и в стандарт языкаСи. Оно показывает, что мы имеем дело с областью памяти, не подлежащей изменению.Наиболее простое и очевидное применение слова const — это введениев программе именованны х к о н с т а н т . Допустим, наша программа29имеет некое свойство, задаваемое в виде числа (примером может служитьограничение на длину строки, читаемой со стандартного ввода). Обычноконкретное число снабжают именем и всю программу выстраивают таким образом, чтобы использовалось это имя, а не числа.
В результате мыможем в любой момент изменить глобальное свойство программы, внесялишь одно изменение в её текст.В классических версиях языка Си для введения имени константыиспользовался препроцессор. Например, ограничение на максимальнуюдлину входного слова можно было задать следующим образом:«d efin e MAX_LINE_LENGTH 130Появление модификатора const позволило в подобных ситуациях обходиться без препроцессора, вводя к о н с т а н т у средствами самого языка:const in t max_line_length = 130;Чтобы понять, почему этот вариант решения лучше предыдущего, рассмотрим следующую гипотетическую ситуацию.
Пусть в нашей программе необходима константа с именем с, равная 15. Введем её средствамипрепроцессора:«d efin e с 15Представим себе теперь, что при работе над текстом программы мы внекий момент забыли о существовании макроса с и ввели, скажем, локальную переменную с таким именем:void f ( i n t i){in t с ;II ...}Вместо объявления in t с компилятор благодаря работе макропроцессора «увидит» строку in t 15 и, разумеется, выдаст сообщение об ошибке.Однако понять, в чем ошибка заключается, будет весьма и весьма нелегко, особенно если мы действительно прочно забыли о существованиимакроса с.Если же константа с будет введена без применения препроцессора:const in t с = 15;то описанная проблема не возникнет, т.
к. имя с, встреченное в функцииf (), будет воспринято компилятором как локальное и никакой ошибкине произойдёт.30В ообщ е, применения м акропроц ессора рекомендуется по мере возм ож н ости и збегать, поскольку м акрои м ена не подчиняю тся обычным д л я я зы к а Си/СиН—|- соглаш ениям об областяхвидимости и другим п равилам .При описании адресных и ссылочных типов модификатор const позволяет указать, что область памяти, на которую указывает/ссылаетсяописываемый объект, не подлежит изменению. Например:const char * р ;/ / р указывает на неизменяемую область памятир = "A s t r i n g " ;/ / всё в порядке, переменная р может изменяться*р = ’ а ’ ;/ / ОШИБКА! Нельзя менять область памяти,/ / на которую указывает рр[5] = ’Ъ’ ;/ / Это также ОШИБКАОтметим ещё раз, что const char *р — это именно у к а з а т е л ь на конс т а н т у , а не к о н с т а н т н ы й у к а з а т е л ь .
Чтобы описать константу адресного типа, необходимо расположить ключевые слова в другом порядке:char b u f[20];char * const p = buf+5;/ / p - константа-указатель, указывающая на/ / шестой элемент массива buf (b u f[5])р++;/ / ОШИБКА, значение р не может быть изменено*р = ’ а ’ ;/ / Всё в порядке, изменяем значение b u f[5]р[5] = ’Ь ’ ; / / Всё в порядке, изменяем значение buf [5+5]Аналогичным образом дело обстоит со ссылочными типами:in t i ;const in t &r = i ; / / ссылка на константуin t x = r+5; / / всё в порядкеi = 7;/ / тоже всё в порядкег = 12; / / ОШИБКА, значение по ссылке г нельзя менятьconst in t j = 5;in t &jr = j ; / / ОШИБКА, нельзя ссылаться/ / на константу обычной ссылкойconst in t &jcr = j ; / / всё в порядкеОтметим, что константные ссылки позволяют передавать в функциив качестве параметра адрес переменной вместо копирования значения,31при этом не позволяя эту переменную менять, как и при обычной передаче по значению.
Это может быть полезно, если параметр представляетсобой класс или структуру, размер которой существенно превышает размер адреса. Так, если в ранее описанном классе Complex вместоComplex operator+(Complex ор2){ return Complex(re+op2.re, im+op2.im); }написатьComplex o p erato rt(co n st Complex &op2){ return Complex(re+op2.re, im+op2.im); }семантика кода не изменится, но физически (на уровне машинного кода)вместо копирования двух полей типа double будет происходить передачаадреса существующей переменной и обращение по этому адресу.§2.8.
К он стан тн ы е методыИспользование модификатора const делает возможной ситуацию, вкоторой нам доступен некоторый к о н с т а н т н ы й объект, то есть переменная типа c la s s или stru c t, которую нельзя изменять. При этом вполневозможно, что все поля объекта скрыты, то есть взаимодействие с объектом возможно только через методы. В то же время метод может, вообщеговоря, изменить внутреннее состояние (то есть значения скрытых полей)объекта, нарушив требование о его неизменности. Поэтому для обеспечения неизменности объекта компилятор вынужден запрещать вызовыметодов. Получается, что с таким объектом мы вообще никак не сможемработать, если не предпримем специальных мер.Для работы с константными объектами в языке С и + + предусмотрены к о н с т а н т н ы е м е т о д ы . Константным считается метод, послезаголовка которого (но перед телом) поставлен модификатор const:c la s s Cl {II ...void method(int a, in t b) const{ /* ....
*/ }u ...В теле такого метода поля объекта доступны, но запрещены действия,изменяющие их или способные привести к их изменению, в том числеприсваивания полям новых значений, присваивания адресов этих полейнеконстантным указателям, передача их в функции по неконстантным32ссылкам и т. п. Таким образом, относительно константного метода известно, что он заведомо не может изменить состояние объекта (значенияего полей). Поэтому константные методы можно без опаски вызыватьдля объектов, которые нельзя изменять. Например, такой код:void f(c o n st Myclass *p) {p->my_method();}будет корректным только в случае, если в классе Myclass методmy_method() объявлен как константный.При написании программ рекомендуется все м етоды , которы епо своем у см ы слу не д ол ж н ы и зм ен ять состояние объекта, обязательн о п ом ечать к ак константные, р а зр е ш ая , таким образом ,в ы зы вать их д л я константных объектов.
В частности, в описанномранее классе Complex все методы, кроме конструкторов, никаких изменений в поля класса не вносят. Чтобы с объектами типа Complex былоудобнее работать, следует пометить все методы класса модификаторомconst:c la s s Complex {double r e , im;p u b lic :Complex(double a_re, double a_im){ re = a_re; im = a_im; }Complex(double a_re){ re = a_re; im = 0; }Complex() { re = 0; im = 0; }double modulo() const{ return s q r t ( r e * r e + im *im ); }double argument() const{ return atan2(im , r e ) ; }double g e t_ re () const { return r e ; }double get_im () const { return im; }Complex o p erato rt(co n st Complex &op2) const{ return Complex(re+op2.re, im+op2.
im ); }// ...>;Отметим, что в теле константного метода не допускаются (для того жеобъекта) вызовы методов, не являющихся константными. Причина такого ограничения очевидна: неконстантный метод может изменить объект,что внутри константного метода делать запрещено.33§2.9. Д естр у к то р ыНаряду с конструкторами, контролирующими процесс создания (инициализации) объектов, в языке С и + + предусмотрены также д е с т р у к т о р ы , предназначенные для контроля над процессом уничтоженияобъекта.Представим себе, что объект в процессе своей деятельности захватывает некий ресурс (например, открывает файл), причем об этом известно только самому объекту, т.