Главная Обратная связь

Дисциплины:

Архитектура (936)
Биология (6393)
География (744)
История (25)
Компьютеры (1497)
Кулинария (2184)
Культура (3938)
Литература (5778)
Математика (5918)
Медицина (9278)
Механика (2776)
Образование (13883)
Политика (26404)
Правоведение (321)
Психология (56518)
Религия (1833)
Социология (23400)
Спорт (2350)
Строительство (17942)
Технология (5741)
Транспорт (14634)
Физика (1043)
Философия (440)
Финансы (17336)
Химия (4931)
Экология (6055)
Экономика (9200)
Электроника (7621)


 

 

 

 



Использование new и delete на примере динамических массивов, стеков, очередей



Операции new[] и delete[] позволяют создавать и удалять динамические массивы, поддерживая при этом иллюзию произвольной размерности. Деятельность по организации динамического массива требует дополнительного внимания, которое окупается важным преимуществом: характеристики массива (операнды операции new) могут не быть константными выражениями. Это позволяет создавать многомерные динамические массивы произвольной конфигурации. Следующий пример иллюстрирует работу с динамическими массивами.

#include <iostream.h>

int fdArr(int **, int, int);

int fdArr(int ***, int, int, int);

// Одноимённые функции. Различаются списками списками параметров.

// Это так называемые перегруженные функции. О них позже.

void main()

{

int i, j;

/* Переменные (!) для описания характеристик массивов.*/

int dim1 = 5, dim2 = 5, dim3 = 10, wDim = dim2;

/*

Организация двумерного динамического массива производится в два этапа.

Сначала создаётся одномерный массив указателей, а затем каждому элементу

этого массива присваивается адрес одномерного массива. Для характеристик

размеров массивов не требуется константных выражений.

*/

int **pArr = new int*[dim1];

for (i = 0; i < dim1; i++) pArr[i] = new int[dim2];

pArr[3][3] = 100;

cout << pArr[3][3] << endl;

fdArr(pArr,3,3);

/*

Последовательное уничтожение двумерного массива…

*/

for (i = 0; i < dim1; i++) delete[]pArr[i];

delete[]pArr;

/*

Организация двумерного "треугольного" динамического массива. Сначала

создаётся одномерный массив указателей, а затем каждому элементу этого

массива присваивается адрес одномерного массива. При этом размер

(количество элементов) каждого нового массива на единицу меньше

размера предыдущего. Заключённая в квадратные скобки переменная в

описателе массива, которая, в данном контексте, является операндом

операции new, позволяет легко сделать это.

*/

int **pXArr = new int*[dim1];

for (i = 0; i < dim1; i++, wDim--) pXArr[i] = new int[wDim];

pXArr[3][3] = 100;

cout << pArr[3][3] << endl;

fdArr(pXArr,3,3);

/*

Последовательное уничтожение двумерного массива треугольной конфигурации…

*/

for (i = 0; i < dim1; i++) delete[]pXArr[i];

delete[]pXArr;

/*

Создание и уничтожение трёхмерного массива требует дополнительной итерации.

Однако здесь также нет ничего принципиально нового.

*/

int ***ppArr;

ppArr = new int**[dim1];

for (i = 0; i < dim1; i++) ppArr[i] = new int*[dim2];

for (i = 0; i < dim1; i++)

{

for (j = 0; j < dim2; j++) ppArr[i][j] = new int[dim3];

}

ppArr[1][2][3] = 750; cout << ppArr[1][2][3] << endl; fdArr(ppArr,1,2,3);

for (i = 0; i < dim1; i++)

{

for (j = 0; j < dim2; j++) delete[]ppArr[i][j];

}

for (i = 0; i < dim1; i++) delete[]ppArr[i];

delete[] ppArr;

}

int fdArr(int **pKey, int index1, int index2)

{

cout << pKey[index1][index2] << endl;

}

int fdArr(int ***pKey, int index1, int index2, int index3)

{

cout << pKey[index1][index2][index3] << endl;

}

 

23)Возможны ситуации, когда для получения доступа к закрытым членам класса вам понадобится функция, не являющаяся членом этого класса. Для достижения этой цели в C++ поддерживаются дружественные функции (friend functions). Дружественные функции не являются членами класса, но тем не менее имеют доступ к его закрытым элементам.

Дружественная функция задается так же, как обычная, не являющаяся членом класса, функция. Однако в объявление класса, для которого функция будет дружественной, необходимо включить ее прототип, перед которым ставится ключевое слово friend. Чтобы понять, как работает дружественная функция, рассмотрим следующую короткую программу:

#include <iostream.h>

int i,j;

class matrix

{

int Row,Col;

double **Value;

public:matrix(int row,int col);

matrix(matrix &m);

~matrix();

int GetRow();

int GetCol();

double &operator()(int row,int col);

friend matrix operator+(matrix &m1,matrix &m2);

friend matrix operator*(matrix &m1,matrix &m2);

friend matrix operator-(matrix &m1,matrix &m2);

matrix operator=(matrix &m);

friend istream &operator>>(istream &istr,matrix &m);

friend ostream &operator<<(ostream &ostr,matrix &m);

};

 

 

24.Использование указателей на методы класса.

Указатель this

Каждый объект содержит свой экземпляр полей класса. Методы места в классе не занимают и не дублируются для каждого объекта. Единственный экземпляр метода используется всеми объектами совместно, поэтому нестатический метод должен "знать", для какого объекта он вызван.

Каждый нестатический метод, помимо явно объявленных параметров, получает еще один скрытый параметр: константный указатель на объект, для которого он вызван. В С++ это указатель обозначается зарезервированным словом this. Когда имя параметра метода совпадает с именем поля класса, доступ к полю выполняется через этот указатель (например, this -> num).

Выражение *this представляет собой разыменование указателя и имеет тип определяемого класса. Обычно это выражение возвращается в качестве результата, если метод возвращает ссылку на свой класс (return *this;).

Для иллюстрации использования указателя this добавим в приведенный выше класс monster новый метод, возвращающий ссылку на наиболее здорового (поле health) из двух монстров, один из которых вызывает метод, а другой передается ему в качестве параметра (метод нужно поместить в секцию public описания класса):

monster & the_best(monster &M){if( health > M.get_health()) return *this;return M;}... monster Vasia(50), Super(200);// Новый объект Best инициализируется значениями полей Supermonster Best = Vasia.the_best(Super);

Работа с объектами чаще всего производится через указатели. Указателю на базовый класс можно присвоить значение адреса объекта любого производного класса, например:

monster *p; // Описывается указатель на базовый классp = new daemon; /* Указатель ссылается на объект производного класса */

Вызов методов объекта происходит в соответствии с типом указателя, а не фактическим типом объекта, на который он ссылается, поэтому при выполнении оператора, например,

p -> draw(1, 1, 1, 1);

будет вызван метод класса monster, а не класса daemon, поскольку ссылки на методы разрешаются во время компоновки программы. Этот процесс называется ранним связыванием. Чтобы вызвать метод класса daemon, можно использовать явное преобразование типа указателя:

((daemon * p)) -> draw(1, 1, 1, 1);

Это не всегда возможно, поскольку в разное время указатель может ссылаться на объекты разных классов иерархии, и во время компиляции программы конкретный класс может быть неизвестен.

 

25.Использование указателей на структуры. Передача массива структур в функцию.

Указатели на структуру

Если функции передается большая структура, то эффективнее передать указатель на эту структуру, нежели копировать ее в стек целиком. Указатель на структуру по виду ничем не отличается от указателей на обычные переменные.

Формат: struct point *pp;

где pp – указатель на структуру типа struct point, *pp – сама структура, (*pp).x и (*pp).y – члены структуры.

Скобки (*pp).x необходимы, так как приоритет операции (.) выше приоритета операции (*). В случае отсутствия скобок *pp.x понимается как *(pp.x).

Инициализация указателя на структуру выполняется так же, как и инициализация указателей других типов: struct point var1, *S1; здесь var1 – структурная переменная, *S1 – указатель на структуру.

Для определения значения указателя ему нужно присвоить адрес уже сформированной структуры:

S1 = &var1;

Теперь возможно еще одно обращение к элементам структуры:

(*S1).name.

Указатели на структуры используются весьма часто, поэтому для доступа к ее полям была введена короткая форма записи. Если р – указатель на структуру, то p → <поле структуры> зволяет обратиться к указанному полю структурной переменной.

Знак → (стрелка) вводится с клавиатуры с помощью двух символов: '–' (минус) и '>' (больше). Например, pp → x; pp → y.

Операторы доступа к полям структуры (.) и (→) вместе с операторами вызова функции () и индексами массива [] занимают самое высокое положение в иерархии приоритетов операций в языке C.

Указатели на структуру используются в следующих случаях:

· доступ к структурам, размещенным в динамической памяти;

· создание сложных структур данных – списков, деревьев;

· передача структур в качестве параметров в функции.

Массивы структур

При необходимости хранения некоторых данных в виде нескольких массивов одной размерности, но разного типа, возможна организация массива структур. Наглядно массив структур можно представить в виде таблицы, где столбцы таблицы представляют собой поля структуры, а строки – элементы массива структур.

Например, имеется два разнотипных массива:

char c[N];

int i[N];

Объявление

struct key

{char c;int i;} keytab[N];

создает тип структуры key и объявляет массив keytab[N], каждый элемент которого есть структура типа key.

Возможна запись:

struct key{char c;int i;};

struct key keytab[N];

Инициализация массива структур выполняется следующим образом:

struct key

{char c;int i;} keytab[]={'a', 0,'b', 0};

В этом примере создается массив на две структуры типа key с именем keytab. Рассмотрим обращение к полю структуры в этом случае – для примера выведем на экран содержимое массива:

for(int i=0;i<2;i++)

printf("%c %d\n",keytab[i].c,keytab[i].i);

При обращении к полю структуры сначала происходит обращение к элементу массива (keytab[i]), а затем только обращение к полю структуры (keytab[i].c).

Доступ к полю элемента массива структур может быть получен через константу-указатель на массив и смещение внутри массива, например, доступ к полю элемента массива с номером i следует записать следующим образом:

(*(keytab+i)).c или (keytab+i)→ c.

Массив структур также может быть передан в функцию в качестве аргумента, передача массива происходит через указатель на массив.

Пример программы, в которой массив точек формируется и выводится на экран с помощью функций:

#include <stdio.h>

#define N 5

struct point

{int x,y;};

void form_mas(struct point* mas, int n)

{printf("Enter koordinates!\n");

for(int i=0;i<n;i++)

{printf("x→");

scanf("%d",&mas[i].x);

printf("y→");

scanf("%d",&mas[i].y);}}

void print_mas(struct point* mas, int n)

{

printf(" x y\n");

for(int i=0; i<n; i++)

printf("%4d %6d\n",(mas+i)→x,(mas+i)→y);

}

int main()

{

struct point Points[N];

form_mas(Points,N);

print_mas(Points,N);

return 0;

}

Функция form_mas() заполняет массив точек, который передан в функцию через указатель mas. Параметр функции n определяет количество элементов массива. Доступ к элементу массива – через операцию . (точка). Выражение &mas[i].x вычисляет адрес элемента структуры mas[i].x, так как операция & имеет более низкий приоритет, чем операции [] и · (точка).

Функция print_mas() выводит массив на экран. Передача массива в функцию происходит также через указатель mas. Параметр n – количество элементов массива. Доступ к элементу массива – через операцию → (стрелка).

 

26.Класс, объектами которого являются множества.

Таким классом является встроенный в язык класс std::setРеализует шаблоны классов контейнеров std::set и std::multiset — сортированные ассоциативные контейнеры или множества.Ассоциативный контейнер set-упорядоченное множество уникальных элементов. При вставке/удалении элементов множества итераторы, указывающие на элементы этого множества, не становятся недействительными. Обеспечивает стандартные операции над множествами типа объединения, пересечения, вычитания. Тип элементов множества должен реализовывать оператор сравнения operator< или требуется предоставить функцию-компаратор. Реализован на основе самобалансирующего дерева двоичного поиска. multiset То же что и set, но позволяет хранить повторяющиеся элементы

Контейнер (container) — хранение набора объектов в памяти.

Итератор (iterator) — обеспечение средств доступа к содержимому контейнера.

 

Пример:

iterator begin ();

const_iterator begin () const;

Возвращает итератор, указывающий на первый элемент в множестве контейнера.

 

/ set::begin/end

#include <iostream>

#include <set>

using namespace std;

int main ()

{

int myints[] = {75,23,65,42,13};

set<int> myset (myints,myints+5);

 

set<int>::iterator it;

cout << "myset contains:";

for ( it=myset.begin() ; it != myset.end(); it++ )

cout << " " << *it;

cout << endl;

return 0;

}

 

 

27.Классы (fstream, ofstream, ifstream, ostream, istream, ios) и представители классов потокового ввода-вывода.

 

Подклассы ofstream, ifstream и fstream объявлены в заголовочном файле

fstream.n и обеспечивают потоки ввода-вывода в дисковые файлы. Эти три класса

предоставляют множество функций для управления вводом и выводом, многие из ко-

торых наследуются от ostream и istream. Полный список этих функций вы можете

найти в документации к компилятору

Класс ofstream, который используется для файлового вывода, имеет несколько

конструкторов; наиболее часто применяется следующий:

ofstream::ofstream(char* pFileName,

int mode = ios::out,

int prot = filebuff::openprot);

 

Класс ifstream работает для ввода почти так же, как ofstream для вывода, что

и демонстрирует приведенный ниже пример.

#include <fstream.h>

Класс fstream похож на комбинацию классов ifstream и ofstream (кстати, он

и наследуется от обоих этих классов). Объект класса fstream может быть создан как

для ввода, так и для вывода.

Классы istrstream, ostrstream и strstream определены в заголовочном файле

с именем strstrea.h или strstream.h.

Классы из strstream.h позволяют использовать операции, определенные для

файлов в классах fstream, для строк в памяти. Это очень похоже на функции

sprintfO Hsscanf() в С.

 

 

 

 



Просмотров 900

Эта страница нарушает авторские права




allrefrs.su - 2024 год. Все права принадлежат их авторам!