Массивы объектов, указатели и ссылки
Массивы объектов создаются так же, как и массивы переменных. Если класс содержит конструктор, массив может быть инициализирован, причем конструктор вызывается столько раз, сколько элементов содержит массив.
#include<iostream.h>//пример 40
class Sample{
int a;
public:
Sample(int n){
a=n;
cout<<”constructor\n”;}
int getA(){return a;}
};
int main() {
Sample ob[4]={1,2,3,4};
Sample *pob=ob;
int i;
for(i=0;i<4;i++) cout<<ob[i].getA ()<< ‘ ‘;
cout<<”\n”;
cout<<pob->getA ();
return 0;
}
Программа выведет 1, 2, 3, 4, конструктор вызывается четыре раза. Затем еще раз будет выведено 1 с помощью указателя pob на первый элемент массива. Список инициализации – это сокращение общей конструкции:
Sample ob[4]={Sample(1),Sample(2),Sample(3),Sample(4)};
Такая конструкция становится основной, если конструктор имеет два и более аргумента. Например:
Sample ob[4]= {Sample(1,2),Sample(3,4),Sample(5,6),Sample(7,8)};
При создании динамических объектов используются указатели на объкт оператор new, который вызывает конструктор и производит инициализацию. Для разрушения динамического объекта применяется оператор delete, который может помещаться в деструкторе. Например:
#include <iostream.h>//пример 41
class Samp {
int i, j;
public:
Samp(){cout<<”конструктор2\n”;}
Samp(int a,int b){i=a;j=b;cout<<”конструктор1\n”;}
void setIJ(int a, int b) { i = a; j = b; }
~Samp() { cout << "удаление...\n"; }
int get() { return i*j; }
};
int main() {
Samp *p01;
int i;
p01 = new Samp(6,5);
Samp *p02;
p02 = new Samp[3];
if(!p01||!p02) {cout << "ошибка выделения памяти\n"; return 1;}
for(i=0; i<3; i++) {
p02[i].setIJ(i, i);
cout << "р02[" << i << "]="<<p02[i].get()<<”\n”;
}
cout << p01->get() << "\n";
delete p01;
delete [ ] p02;
return 0;
}
Результат:
конструктор1
конструктор2
конструктор2
конструктор2
р02[0]=0
p02[1]=1
p02[2]=4
удаление...
удаление...
удаление...
удаление...
Деструктор вызывается 4 раза: по одному разу на каждый элемент массива и один раз для объекта р01.
Ссылки
Ссылка является скрытым константным указателем и работает как другое имя переменной. Ее можно использовать для передачи аргументов в функцию и возврата значений обратно. При передаче объекта через ссылку в функцию сообщается адрес объекта, а его копия не делается. Это уменьшает вероятность ошибок, связанных с выделением динамической памяти и вызовом деструктора. Аналогично, при возврате ссылки на объект из функции также не делается копия объекта.
При передаче функции объекта в качестве параметра может возникнуть ошибка из-за разрушения деструктором копии объекта, которая должна быть исправлена созданием конструктора копирования. В этой ситуации лучше использовать функцию, возвращающую ссылку на объект. Например:
// защищенный двумерный массив
#include <iostream.h>//пример 42
#include <stdlib.h>
class Array {
int isize, jsize;
int *p;
public:
Array(int i, int j) {
p = new int [ i * j ];
if(!p) {
cout << "Ошибка выделения памяти\n";
exit(1); }
isize = i;
jsize = j;
}
int &put(int i, int j);
int get(int i, int j);
};
/* возврат ссылки на элемент массива, в который необходимо выполнить запись */
int &Array::put(int i, int j) {
if(i<0 || i>=isize || j<0 || j>=jsize) {
cout << "Ошибка, нарушены границы массива!!!\n";
exit(1);
}
return p[i * jsize + j]; // возврат ссылки на p[ i ] [ j ]
}
// получение значения из массива
int Array::get(int i, int j) {
if(i<0 || i>=isize || j<0 || j>=jsize) {
cout << "Ошибка, нарушены границы массива!!!\n";
exit(1);
}
return p[i * jsize + j]; // возврат символа
}
int main() {
Array a(2, 3);
int i, j;
for(i=0; i<2; i++)
for(j=0; j<3; j++)
a.put(i, j) = i + j;
for(i=0; i<2; i++)
for(j=0; j<3; j++)
cout << a.get(i, j) << ' ';
// генерация ошибки нарушения границ массива
a.put(10, 10);
return 0;
}