Г. Шилтд - Самоучитель C++ (PDF) (1114887), страница 17
Текст из файла (страница 17)
Объясните, почему это необходимо для некоторых компиляторов?7. Дан следующий класс, покажите, как добавить дружественную функциюisnegQ, которая получает один параметр типа myclass и возвращает true,если значение num отрицательно и false — в противном случае.class myclass {int num;public:myclass(int x} { num = x; }J;8. Может ли дружественная функция быть дружественной более чем одномуклассу?4.1. Массивы объектовКак уже отмечалось ранее, объекты — это переменные, и они имеют те жевозможности и признаки, что и переменные любых других типов. Поэтомувполне допустимо упаковывать объекты в массив.
Синтаксис объявлениямассива объектов совершенно аналогичен тому, который используется дляобъявления массива переменных любого другого типа. Более того, доступ кмассивам объектов совершенно аналогичен доступу к массивам переменныхлюбого другого типа.Глава 4. Массивы, указатели и ссылки109Примеры!PWJ>••"1.
Пример массива объектов:^include <iostream>using namespace std;class samp {int a;public:void set_a(int n) ( a = n; }int get_a{) { return a; }int(mainOsamp ob[4] ;int i;for(i=0; i<4; i++) ob[ i ] . s e t _ a ( i ) ;for(i=0; i<4; i++) cout « ob[ i ] . g e t _ a ( ) ;cout « "\n" ;return 0;В этой программе создается массив из четырех элементов типа samp, которым затем присваиваются значения от 0 до 3. Обратите внимание на то, каквызываются функции-члены для каждого элемента массива.
Имя массива, вданном случае ob, индексируется; затем применяется оператор доступа кчлену, за которым следует имя вызываемой функции- члена.2. Если класс содержит конструктор, массив объектов может быть инициализирован. Например, здесь объект ob является инициализируемым массивом:// Инициализация массива^include <iostream>using namespace std;class samp {int a;public :samp (int n) { a = n; }int get_a() { return a; }};int main ( ){samp o b [ 4 ) = { -1, -2, -3,int i;-4 };110_СамоучительC++for{i=0; i<4; 14+) cout « ob [ i ].get_a() « ' ';cout « "\n";return 0;Эта программа выводит на экран —1 —2 —3 —4. В этом примере значения от— 1 до —4 передаются объекту ob конструктором.Фактически синтаксис списка инициализации — это сокращение следующейконструкции (впервые показанной в главе 2):samp ob[4] = { samp(-l) , samp (-2} , samp (-3) , samp (-4) } ;Однако при инициализации одномерного массива общепринятой является таформа записи, которая была показана в программе (хотя, как вы дальше увидите, такая форма записи будет работать только с теми массивами, конструктор которых имеет единственный аргумент).3.
Вы также можете работать с многомерными массивами объектов. Например,эта программа создает двумерный массив объектов и инициализирует его:// Создание двумерного массива объектов^include <iostream>using namespace std;class samp {int a;public:sampfint n) { a = n; }int get_a() { return a; }int mainOsamp ob[4] [2] = {1, 2,3, 4,5, 6,1, 8};int i;for(i=0; i<4; i++) {cout « ob[i] [0],get__a() « ' 'cout « ob[i] [l].get_a() « "\n}cout « "\n";return 0;Глава 4. Массивы, указатели и ссылки111Эта программа выводит на экран следующее:123 45 б7 84. Как вы знаете, конструктор может иметь более одного аргумента. При инициализации массива объектов с таким конструктором вы должны использовать упоминавшуюся ранее альтернативную форму инициализации. Начнем спримера:^include <iostream>using namespace std;class samp (int a, b;public:samp (int n, int rn) { a = n; b = m; }int get_a() { return a; }int get_b() { return b; }int main ( )Isamp ob [ 4 ] [ 2 ] = {sampU, 2 ) , samp(3, 4 ) ,sarnp(5, 6), samp(7, 8),samp(9, 10), samp (11, 12),samp(13, 1 4 ) , sarnp(15, 16)1;int i.;for(i=0;coutcoutcoutcouti<4« ob[i]« ob[i]« ob[i][0].get_a() «[0].get_b() « "\n"[l].get_a(J «ob[i] [ l ] .
g e t _ b ( ) « "\n"Jcout « "\n";return 0;В этом примере конструктор samp имеет два аргумента. Здесь массив ob объявляется и инициализируется в функции mainQ с помощью прямых вызовов конструктора samp. Это необходимо, поскольку формальный синтаксис С++позволяет одновременное использование только одного аргумента в разделяе-112Самоучитель С++мом запятыми списке. При этом невозможно задать, например, два или болееаргумента в каждом элементе списка. Поэтому если вы инициализируете массивобъектов, имеющих конструктор с более чем одним аргументом, то вам следуетпользоваться длинной формой инициализации вместо ее сокращенной формы.Вы всегда можете использовать длинную форму инициализации, даже приналичии у объекта конструктора с одним аргументом. Хотя для этого случаябольше подходит сокращенная форма.Предыдущая программа выводит на экран следующее:I 23 45 67 89 10II 1213 1415 16Упражнения1.
Используя следующее объявление класса, создайте массив из 10 элементов иинициализируйте переменную ch значениями от А до J. Покажите, что массив на самом деле содержит эти значения.^include <iostream>using namespace std;class letters {char ch;public:letters (char c) { ch = c; }char get_ch() { return ch; }2.
Используя следующее объявление класса, создайте массив из 10 элементов,инициализируйте переменную num значениями от 1 до 10, а переменнуюsqr — квадратом num.^include <iostream>using namespace std;Глава 4. Массивы, указатели и ссылки113class squares {int num, sqr;public:squares(int a, int b) { num = a; sqr = b; }void show() {cout « num « ' ' « sqr « "\n"; }3. Измените инициализацию переменной ch из упражнения 1 так, чтобы использовать ее длинную форму (т. е. чтобы конструктор letters явно вызывалсяв списке инициализации).4.2.
Использование указателейна объектыКак отмечалось в главе 2, доступ к объекту можно получить через указательна этот объект. Как вы знаете, при использовании указателя на объект кчленам объекта обращаются не с помощью оператора точка (.), а с помощьюоператора стрелка (->).Арифметика указателей на объект аналогична арифметике указателей наданные любого другого типа: она выполняется относительно объекта. Например, если указатель на объект инкрементируется, то он начинает указывать на следующий объект. Если указатель на объект декрементируется, тоон начинает указывать на предыдущий объект.Примеры- >г1.
Пример арифметики указателей на объекты;// Указатели на объекты#include <iostream>using namespace std;class samp (int a, b;public:samp {int n, int m) { a = n; b = m; }int get_a() { return a; }int get_b() { return b; }int main ( )samp ob[4]samp { 1 , 2 ) ,114Самоучитель C++samp(3, 4),samp(5, 6),samp(7, 8)int i;samp *p;p = ob; // получение адреса начала массиваfor(i=0;coutcoutP-H-;i<4; i++) {« p->get_a(} « ' ';« p->get_b() « "\n";// переход к следующему объектуcout « "\n";return 0;Эта программа выводит на экран следующее:1 23 45 б7 8•Как видно из результата, при каждом инкрементировании указателя р онуказывает на следующий объект массива.1. Перепишите пример 1 так, чтобы на экран выводилось содержимое массиваob в обратном порядке.2.
Измените пример 3 раздела 4.1 так, чтобы получить доступ к двумерномумассиву через указатель. Подсказка: в C++, как и в С, все массивы хранятсянепрерывно, слева направо, от младшего элемента к старшему.4.3. Указатель thisC++ содержит специальный указатель this. Это указатель, который автоматически передается любой функции-члену при ее вызове и указывает на объект, генерирующий вызов. Например, рассмотрим следующую инструкцию:ob.fl(); // предположим, что ob — это объектГлава 4.
Массивы, указатели и ссылкиП5Функции fl() автоматически передается указатель на объект ob. Этот указатель и называется this.Важно понимать, что указатель this передается только функциям-членам.Дружественным функциям указатель this не передается.1. Как вы уже видели, если функция-член работает с другим членом того жекласса, она делает это без уточнения имени класса или объекта. Например,исследуйте эту короткую программу, в которой создается простой классinventory:/ / Демонстрация указателя this#include <j_ostream>^include <cstring>using namespace std;class inventory {char item[20];double cost;int on_hand;public:inventory(char *i, double c, int o){strcpy(item, i);cost = c;on_hand = o;}void show();};void inventory::show(){cout « item;cout « ": $" « cost;cout « " On hand: " « on hand « "\n";int main()inventory ob("wrench", 4.95, 4);ob.show();return 0;1_116СамоучительОбратите внимание, что внутри конструктора inventoryO и функции-членаshow() переменные- члены item, cost и onjiand упоминаются явно.
Так происходит потому, что функция-член может вызываться только в связи с объектом. Следовательно, в данном случае компилятор "знает", данные какогообъекта имеются в виду.Однако имеется еще более тонкое объяснение. Если вызывается функциячлен, ей автоматически передается указатель this на объект, который является источником вызова. Таким образом, предыдущую программу можно переписать так:// Демонстрация указателя this#include <iostream>^include <cstring>using namespace std;class inventory {char item[20] ;double cost;int on_hand;public:inventory (char *i, double c,{strcpy (this->item, i) ; //this->cost = с;//this->on_hand = о;//1void show {) ;\.int o)доступ к членучерезуказатель thisvoid inventory: : show (){cout « this->item; // использование this для доступа к членамcout « ": $" « this->cost;cout « " On hand: " « this->on_hand « "\n";intmain(){inventory ob( "wrench", 4 .
9 5 ,4);ob . show ( ) ;return 0;Здесь к переменным-членам объекта ob осуществляется прямой доступ черезуказатель this. Таким образом, внутри функции show() следующие две инструкции равнозначны:Глава 4. Массивы, указатели и ссылки117cost = 123.23;thi 3 ->cost = 123.23;На самом деле первая форма — это сокращенная запись второй.Пока, наверное, еще не родился программист C++, который бы использовалуказатель this для доступа к членам класса так, как было показано, посколькусокращенная форма намного проще, но здесь важно понимать, что под этимсокращением подразумевается.Использовать указатель this можно no-разному.