Использование new delete для реализации массивов
Оператор new позволяет выделять память под массивы. Он возвращает
указатель на первый элемент массива в квадратных скобках. При выделении памяти под многомерные массивы все размерности кроме крайней левой должны быть константами. Первая размерность может быть задана переменной, значение которой к моменту использования new известно пользователю, например:
k=3;
int *p[]=new int[k][5]; // ошибка cannot convert from 'int (*)[5]' to 'int *[]'
int (*p)[5]=new int[k][5]; // верно
При выделении памяти под объект его значение будет неопределенным. Однако объекту можно присвоить начальное значение.
int *a = new int (10234);
Этот параметр нельзя использовать для инициализации массивов. Однако
на место инициализирующего значения можно поместить через запятую список
значений, передаваемых конструктору при выделении памяти под массив (мас-
сив новых объектов, заданных пользователем). Память под массив объектов
может быть выделена только в том случае, если у соответствующего класса
имеется конструктор, заданный по умолчанию.
class matr
{ int a;
float b;
public:
matr(){}; // конструктор по умолчанию
matr(int i,float j): a(i),b(j) {}
~matr(){};
};
int main()
{ matr mt(3,.5);
matr *p1=new matr[2]; // верно р1 − указатель на 2 объекта
matr *p2=new matr[2] (2,3.4); // неверно, невозможна инициализация
matr *p3=new matr (2,3.4); // верно р3 – инициализированный объект
}
class A
{ int i; // компонента-данное класса А
public:
A(){} // конструктор класса А
~A(){} // деструктор класса А
};
int main()
{ A *a,*b; // описание указателей на объект класса А
float *c,*d; // описание указателей на элементы типа float
a=new A; // выделение памяти для одного объекта класса А
b=new A[3]; // выделение памяти для массива объектов класса А
c=new float; // выделение памяти для одного элемента типа float
d=new float[4]; // выделение памяти для массива элементов типа float
delete a; // освобождение памяти, занимаемой одним объектом
delete [] b; // освобождение памяти, занимаемой массивом объектов
delete c; // освобождение памяти одного элемента типа float
delete [] d; } // освобождение памяти массива элементов типа float
Организация внешнего доступа к локальным компонентам класса(friend)
Мы уже познакомились с основным правилом ООП – данные (внутренние
переменные) объекта защищены от воздействий извне и доступ к ним можно
получить только с помощью функций (методов) объекта. Но бывают такие слу-
чаи, когда нам необходимо организовать доступ к данным объекта, не исполь-
зуя его интерфейс (функции). Конечно, можно добавить новую public-функцию
к классу для получения прямого доступа к внутренним переменным. Однако в
большинстве случаев интерфейс объекта реализует определенные операции, и
новая функция может оказаться излишней. В то же время иногда возникает не-
обходимость организации прямого доступа к внутренним (локальным) данным
двух разных объектов из одной функции. При этом в С++ одна функция не мо-
жет быть компонентой двух различных классов.
Для реализации этого в С++ введен спецификатор friend. Если некоторая
функция определена как friend-функция для некоторого класса, то она:
- не является компонентой-функцией этого класса;
- имеет доступ ко всем компонентам этого класса (private, public и protected).
Ниже рассматривается пример, когда внешняя функция получает доступ к
внутренним данным класса.
#include <iostream>
using namespace std;
class kls
{ int i,j;
public:
kls(int i,int J) : i(I),j(J) {} // конструктор
int max() {return i>j? i : j;} // функция-компонента класса kls
friend double fun(int, kls&); // friend-объявление внешней функции fun
};
double fun(int i, kls &x) // внешняя функция
{ return (double)i/x.i;
}
main()
{ kls obj(2,3);
cout << obj.max() << endl;
сout << fun(3,obj) << endl;
return 1;
}
Ссылки.Параметры ссылки
В С(С++) известны три способа передачи данных в функцию: по значе-
нию, посредством указателя и используя ссылки. тип & имя_ссылки = инициализатор.
Ссылку нельзя объявить без ее инициализации. То есть ссылаться всегда
можно на некоторый существующий объект. Можно выделить следующие раз-
личия ссылок и указателей. Во-первых, невозможность существования нулевых
ссылок подразумевает, что корректность их не требуется проверять. А при использовании указателя требуется проверять его на ненулевое значение. Во-вторых, указатели могут указывать на различные объекты, а ссылка всегда на один объект, заданный при ее инициализации. Если требуется предоставить возможность функции изменять значения
передаваемых в нее параметров, то в языке С они должны быть объявлены либо
глобально, либо работа с ними в функции осуществляется через передаваемые в
нее указатели на эти переменные. В С++ аргументы в функцию можно переда-
вать также и через ссылку. Для этого при объявлении функции перед парамет-
ром ставится знак &.
void fun1(int,int);
void fun2(int &,int &);
int main()
{ int i=1,j=2; // i и j – локальные параметры
cout << "\n адрес переменных в main() i = "<<&i<<" j = "<<&j;
cout << "\n i = "<<i<<" j = "<<j;
fun1(i,j);
cout << "\n значение i = "<<i<<" j = "<<j;
fun2(i,j);
cout << "\n значение i = "<<i<<" j = "<<j;
}
void fun1(int i,int j)
{ cout << "\n адрес переменных в fun1() i = "<<&i<<" j = "<<&j;
int a; // при вызове fun1 i и j из main() копируются
a=i; i=j; j=a; // в стек в переменные i и j при возврате в main()
} // они просто теряются
void fun2(int &i,int &j)
{ cout << "\n адрес переменных в fun2() i = "<<&i<<" j = "<<&j;
int a; // здесь используются ссылки на переменные i и j из
a=i; i=j; j=a; } // main() (вторые их имена) и таким образом действия
// в функции производятся с теми же переменными i и j
Ссылки.Независимые ссылки
В языке С++ ссылки могут быть использованы не только для реализации
механизма передачи параметров в функцию. Они могут быть объявлены в про-
грамме наряду с обычными переменными, например:
#include <iostream>
using namespace std;
int main()
{ int i=1;
int &j=i; // j – ссылка (второе имя) переменной i
cout << "\n адрес переменных i = "<<&i<<" j = "<<&j;
cout << "\n значение i = "<<i<<" j = "<<j;
j=5; //
cout << "\n адрес переменных i = "<<&i<<" j = "<<&j;
cout << "\n значение i = "<<i<<" j = "<<j;
return 0;
}
В результате работы программы будет получено:
адрес переменных i = 0xадрес1 j = 0xадрес2
значение i = 1 j = 1
адрес переменных i = 0xадрес1 j = 0xадрес2
значение i =5 j = 5
В этом случае компилятор создает временный объект j, которому при-
сваивается адрес ранее созданного объекта i. Далее j может быть использовано
как второе имя i.