Использование 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.