Г. Шилтд - Самоучитель C++ (PDF) (1114887), страница 19
Текст из файла (страница 19)
В следующей программе размещается массив целых:// Простой пример использования операторов new и delete#include <iostream>using namespace std;int main ( )int *p;p = new int [5]; // выделение памяти для 5 целых_124I/ Убедитесь,Самоучительчто памятьвыделенаiff!p) !cout « "Ошибка выделения памяти\п" ;return 1;int i ;for(i=0;delete [] p; // освобождение памятиreturn 0;}Эта программа выводит на экран следующее:ЭтоЭтоЭтоЭтоЭтоцелое,целое,целое,целое,целое,нананананакотороекотороекотороекотороекотороеуказываетуказываетуказываетуказываетуказываетр[0]:р[1]:р[2]:р[3]:р[4]:О12344. В следующей программе создается динамический массив объектов:// Динамическое выделение памяти для массива объектов^include <iostream>using namespace std;class samp (int i, j;public:void set_ij(int a, int b) f i = a; j = b; }int get_product() { return i*j; }I;int main()isamp *p;int i ;p = new samp [10]; // размещение массива объектовcout « "Ошибка выделения памяти\n";return 1;C++Глава 4.
Массивы, указатели и ссылки125for (i-0; i<10;p [ i ] .set_ij (i,for(i=0;cout « "Содержимое [" « i « "] равно:cout « p[i] .get_product ( ) « "\n";delete [] p;return 0;Эта программа выводит на экран следующее:СодержимоеСодержимоеСодержимоеСодержимоеСодержимоеСодержимоеСодержимоеСодержимоеСодержимоеСодержимое[0][1][2][3][4][5][6][7][8][9]равно:равно:равно:равно:равно:равно:равно:равно:равно:равно:О1491625364964815. В новой версии предыдущей программы в нее вводится деструктор samp итеперь при освобождении памяти, обозначенной указателем р, для каждогоэлемента массива вызывается деструктор:// Динамическое выделение памяти для массива объектов^include <iostream>using namespace std;class samp {int i, j;public:void set_ij (int a, int b) { i = a; j - b; }-sampO { cout « "Удаление объекта ...
\n"; }int get_product ( ) { return i*j; }int main ( )/samp *p;int i;p = new samp [10]; // размещение массива объектовcout « "Ошибка выделения памяти\п";return 1;7 26Самоучитель C++for(i=0; icout « "Содержимое [" « i « "] равно:";cout « p[i] .get_product (} « "\n";}delete [J p;return 0;Эта программа выводит на экран следующее:Содержимое [0] равно:Содержимое [ 1 ] равно :Содержимое [2] равно:Содержимое [3] равно:Содержимое [4] равно:Содержимое [5] равно:Содержимое [ 6 ] равно :Содержимое [7] равно:Содержимое [8] равно:Содержимое [9] равно:Удаление объекта .
. .Удаление объекта . . .Удаление объекта...Удаление объекта . . .Удаление объекта. . .Удаление объекта. . .Удаление объекта . . .Удаление объекта. . .Удаление объекта. . .Удаление объекта. . .О14916253б496481Как видите, деструктор samp вызывается десять раз — по разу на каждыйэлемент массива.1. Переделайте данную программу так, чтобы в ней использовался оператор new.char *p;р - ( char *) rnalloc(lOO) ;// ...strcpyip, "Это проверка");Подсказка.
Строка — это просто массив символов.Глава 4. Массивы, указатели и ссылки1272. Используя оператор new, покажите, как динамически разместить переменнуютипа double и передать ей начальное значение —123.0987.4.6. СсылкиВ C++ есть элемент, родственный указателю — это ссылка (reference). Ссылка является скрытым указателем и во всех случаях, и для любых целей ееможно употреблять просто как еще одно имя переменной. Ссылку допустимо использовать тремя способами.
Во-первых, ссылку можно передать вфункцию. Во-вторых, ссылку можно возвратить из функции. Наконец,можно создать независимую ссылку. В книге рассмотрены все эти применения ссылки, начиная со ссылки в качестве параметра функции.Несомненно, наиболее важное применение ссылки -— это передача ее в качестве параметра функции.
Чтобы помочь вам разобраться в том, что такоепараметр -ссылка и как он работает, начнем с программы, в которой параметром является указатель (а не ссылка):^include <iostream>using namespace std;void f ( i n t *n) ; // использование параметра-указателяint main ( )int i = 0;cout « "Новое значение i: " « i « ' n V ;return 0;}void f (int *n)(*n = 100; // занесение числа 100 в аргумент,//на который указывает указатель п\Здесь функция f() загружает целое значение 100 по адресу, который обозначен указателем п.
В данной программе функция f() вызывается из функцииmain() с адресом переменной i. Таким образом, после выполнения функцииf() переменная i будет содержать число 100.В этой программе показано, как использовать указатель для реализации механизма передачи параметра посредством вызова по ссылке (call byreference). В программах С такой механизм является единственным спосо-128_СамоучительC++бом добиться вызова функции по ссылке.
Однако в C++ с помощью параметра-ссылки можно полностью автоматизировать весь процесс. Чтобы узнать, как это сделать, изменим предыдущую программу. В ее новой версиииспользуется параметр -ссылка:^include <iostream>using namespace std;void f (int &n) ; // объявление параметра -ссылкиint main ( )(int i = 0;f (i);cout « "Новое значение i: " « i « 'n\';return 0;// Теперь в функции f ( ) используется параметр-ссылкаvoid f ( i n t &n)(// отметьте, что в следующей инструкции знак * не требуетсяп = 100; // занесение числа 100 в аргумент,// используемый при вызове функции f{)Тщательно проанализируйте эту программу. Во-первых, для объявления параметра-ссылки перед именем переменной ставится знак амперсанда (&).
Такимобразом, переменная п объявляется параметром функции f(). Теперь, поскольку переменная п является ссылкой, больше не нужно и даже неверноуказывать оператор *. Вместо него всякий раз, когда переменная п упоминается внутри функции f(), она автоматически трактуется как указатель на аргумент, используемый при вызове функции f(). Это значит, что инструкцияфактически помещает число 100 в переменную, используемую при вызовефункции f(), каковой в данном случае является переменная i. Далее, привызове функции f() перед аргументом не нужно ставить знак &.
Вместоэтого, поскольку функция f() объявлена как получающая параметр-ссылку,ей автоматически передается адрес аргумента.Повторим, при использовании параметра-ссылки компилятор автоматически передает функции адрес переменной, указанной в качестве аргумента.Нет необходимости (а на самом деле и не допускается) получать адрес аргумента с помощью знака &. Более того, внутри функции компилятор автома-Глава4.Массивы,указателииссылки_129тически использует переменную, на которую указывает параметр -ссылка,Нет необходимости (и опять не допускается) ставить знак *. Таким образом,параметр-ссылка полностью автоматизирует механизм передачи параметрапосредством вызова функции по ссылке.Важно понимать следующее: адрес, на который указывает ссылка, вы изменить не можете.
Например, если в предыдущей программе инструкциянаходилась бы внутри функции f(), ссылка п по-прежнему указывала бы напеременную i в функции main(). Вместо инкрементирования адреса, на который указывает ссылка п, эта инструкция инкрементирует значение переменной (в данном случае это переменная i).Параметры- ссылки имеют несколько преимуществ по сравнению с аналогичными (более или менее) альтернативными параметрами-указателями.Во-первых, с практической точки зрения нет необходимости получать ипередавать в функцию адрес аргумента.
При использовании параметрассылки адрес передается автоматически. Во-вторых, по мнению многихпрограммистов, параметры-ссылки предлагают более понятный и элегантный интерфейс, чем неуклюжий механизм указателей. В-третьих, как выувидите в следующем разделе, при передаче объекта функции через ссылкукопия объекта не создается. Это уменьшает вероятность ошибок, связанныхс построением копии аргумента и вызовом ее деструктора.' Примеры"—^1.
Классическим примером передачи аргументов по ссылке является функция,меняющая местами значения двух своих аргументов. В данном примере вфункции swapargs() ссылки используются для того, чтобы поменять местамидва ее целых аргумента:^include <iostream>using namespace std;void swapargs(int &x, int £y) ;intmain()iint i, j;i = 10;j = 19;cout « "i: " « i « ", ";cout « "j: " « j « "\n";swapargs(i, j) ;730Самоучитель C++cout « "После перестановки: ";cout « "i: " « i « ", ";cout « "j: " « j « "\n";return 0;void swapargs(int &x, int &y}{int t;t = x;x = y;Если бы при написании функции swapargsQ вместо ссылок использовалисьуказатели, то функция выглядела бы следующим образом:void swapargsfint *x, int *y)|int t;с —л,J- — *v .*х = *у;*у = t;Как видите, благодаря использованию ссылок в функции swapargs(), отпадаетнеобходимость указывать знак *.2.
В следующей программе с помощью функции round() округляется значениетипа double. Округляемое значение передается по ссылке.^include <iostream>^include <cmath>using namespace std;void round (double &num) ;int main ( )iouble i = 100.4;:>ut « i « "после округления ";>und ( i ) ;i_ .at « i « "\n";л = 10.9;cout « i « "после округления ";round ( i ) ;cout « i « "\n";Глава4.Массивы,указателииссылки_131return 0;}void round (double snum){double frac;double val;// разложение num на целую и дробную частиfrac = modf(num, &val);ifffrac < 0.5) num = val;else num = val + 1.0;В функции rou n (1 0 для разложения числа на целую и дробную части указанаредко используемая функция modf().
Возвращаемым значением этой функции является дробная часть; целая часть помещается в переменную, на которую указывает второй параметр функции modf().Упражнения!^> функцию neg(), которая меняет знак своего целого параметра на1. Напишитепротивоположный. Напишите функцию двумя способами: первый — используя параметр-указатель и второй — параметр-ссылку.