|
Программирование >> Поддержка объектно-ориентированного программирования
Здесь определяется новый тип, называемый address, который задает почтовый адрес. Определение не является достаточно общим, чтобы учесть все случаи адресов, но оно вполне пригодно для примера. Обратите внимание на точку с запятой в конце определения: это один из немногих в С++ случаев, когда после фигурной скобки требуется точка с запятой, поэтому про нее часто забывают. Переменные типа address можно описывать точно так же, как и любые другие переменные, а с помощью операции . (точка) можно обращаться к отдельным членам структуры. Например: address jd; jd.name = Jim Dandy ; jd.number = 61; Инициализировать переменные типа struct можно так же, как массивы. Например: address jd = { Jim Dandy , 61, South Street , New Providence , {N,J}, 7974 Но лучше для этих целей использовать конструктор ($$5.2.4). Отметим, что jd.state нельзя инициализировать строкой NJ . Ведь строки оканчиваются нулевым символом \0, значит в строке NJ три символа, а это на один больше, чем помещается в jd.state. К структурным объектам часто обращаются c помощью указателей, используя операцию ->. Например: void print addr(address* p) cout << p->name << \n << p->number << << p->street << \n << p->town << \n << p->state[0] << p->state[1] << << p->zip << \n; Объекты структурного типа могут быть присвоены, переданы как фактические параметры функций и возвращены функциями в качестве результата. Например: address current; address set current(address next) address prev = current; current = next; return prev; Другие допустимые операции, например, такие, как сравнение (== и !=), неопределены. Однако пользователь может сам определить эти операции (см. главу 7). Размер объекта структурного типа не обязательно равен сумме размеров всех его членов. Это происходит по той причине, что на многих машинах требуется размещать объекты определенных типов, только выравнивая их по некоторой зависящей от системы адресации границе (или просто потому, что работа при таком выравнивании будет более эффективной ). Типичный пример - это выравнивание целого по словной границе. В результате выравнивания могут появиться дырки в структуре. Так, на уже упоминавшейся машине автора sizeof(address) равно 24, а не 22, как можно было ожидать. Следует также упомянуть, что тип можно использовать сразу после его появления в описании, еще до того, как будет завершено все описание. Например: struct link{ link* previous; link* successor; Однако новые объекты типа структуры нельзя описать до тех пор, пока не появится ее полное описание. Поэтому описание struct no good { no good member; является ошибочным (транслятор не в состоянии установить размер no good). Чтобы позволить двум (или более) структурным типам ссылаться друг на друга, можно просто описать имя одного из них как имя некоторого структурного типа. Например: struct list; будет определено позднее struct link { link* pre; link* suc; list* member of; struct list { link* head; Если бы не было первого описания list, описание члена link привело бы к синтаксической ошибке. Можно также использовать имя структурного типа еще до того, как тип будет определен, если только это использование не предполагает знания размера структуры. Например: class S; S - имя некоторого типа extern S a; S f(); void g(S); Но приведенные описания можно использовать лишь после того, как тип S будет определен: void h() S a; ошибка: S - неописано f(); ошибка: S - неописано g(a); ошибка: S - неописано 2.3.9 Эквивалентность типов Два структурных типа считаются различными даже тогда, когда они имеют одни и те же члены. Например, ниже определены различные типы: struct s1 { int a; }; struct s2 { int a; }; В результате имеем: s1 x; s2 y = x; ошибка: несоответствие типов Кроме того, структурные типы отличаются от основных типов, поэтому получим: s1 x; int i = x; ошибка: несоответствие типов Есть, однако, возможность, не определяя новый тип, задать новое имя для типа. В описании, начинающемся служебным словом typedef, описывается не переменная указанного типа, а вводится новое имя для типа. Приведем пример: typedef char* Pchar; Pchar p1, p2; char* p3 = p1; Это просто удобное средство сокращения записи. 2.3.10 Ссылки Ссылку можно рассматривать как еще одно имя объекта. В основном ссылки используются для задания параметров и возвращаемых функциями значений , а также для перегрузки операций (см.$$7). Запись X& обозначает ссылку на X. Например: int i = int& r int x = r = 2; i; r; r x i и i = 1 = 2; ссылаются на одно и то же целое Ссылка должна быть инициализирована, т.е. должно быть нечто, что она может обозначать. Следует помнить, что инициализация ссылки совершенно отличается от операции присваивания. Хотя можно указывать операции над ссылкой, ни одна из них на саму ссылку не действует, например, int ii = 0; int& rr = ii; rr++; ii увеличивается на 1 Здесь операция ++ допустима, но rr++ не увеличивает саму ссылку rr; вместо этого ++ применяется к целому, т.е. к переменной ii. Следовательно, после инициализации значение ссылки не может быть изменено: она всегда указывает на тот объект, к которому была привязана при ее инициализации. Чтобы получить указатель на объект, обозначаемый ссылкой rr, можно написать &rr. Очевидной реализацией ссылки может служить постоянный указатель, который используется только для косвенного обращения. Тогда инициализация ссылки будет тривиальной, если в качестве инициализатора указан адрес (т.е. объект, адрес которого можно получить; см. $$R.3.7). Инициализатор для типа T должен быть адресом. Однако, инициализатор для &T может быть и не адресом, и даже не типом T. В таких случаях делается следующее: [1] во-первых, если необходимо, применяется преобразование типа (см.$$R.8.4.3); [2] затем получившееся значение помещается во временную переменную; [3] наконец, адрес этой переменной используется в качестве инициализатора ссылки. Пусть имеются описания: ошибка: нужен адрес double& dr = 1; const double& cdr = Это интерпретируется так: double* cdrp; double temp; temp = double(1); cdrp = &temp; нормально ссылка, представленная как указатель Ссылки на переменные и ссылки на константы различаются по следующей причине: в первом случае создание временной переменной чревато ошибками, поскольку присваивание этой переменной означает присваивание временной переменной, которая могла к этому моменту исчезнуть. Естественно, что во втором случае подобных проблем не существует. и ссылки на константы часто используются как параметры функций (см.$$R.6.3). Ссылка может использоваться для функции, которая изменяет значение своего параметра. Например: void incr(int& aa) { aa++; } void f() int x = 1; incr(x); x = 2 По определению передача параметров имеет ту же семантику, что и инициализация, поэтому при вызове функции incr ее параметр aa становится другим именем для x. Лучше, однако, избегать изменяющих свои параметры функций, чтобы не запутывать программу. В большинстве случаев предпочтительнее, чтобы функция возвращала результат явным образом, или чтобы использовался
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |