Управление памятью: операции new и delete. Указатель this.




Управление памятью (new, delete). Основные отличия между статическим и динамическим выделением памяти следующие:• статические объекты обозначаются именованными переменными, и действия над этими объектами производятся напрямую, с использованием их имен. Динамические объекты не имеют собственных имен, и действия над ними производятся косвенно, с помощью указателей;• выделение и освобождение памяти под статические объекты производится компилятором автоматически. Программисту не нужно самому заботиться об этом. Выделение и освобождение 97 памяти под динамические объекты целиком и полностью возлагается на программиста. Это достаточно сложная задача, при решении которой легко наделать ошибок. Для манипуляции динамически выделяемой памятью служат операторы new и delete. Оператор new имеет две формы. Первая форма выделяет память под единичный объект определенного типа: int *pint = new int(1024); Здесь оператор new выделяет память под безымянный объект типа int, инициализирует его значением 1024 и возвращает адрес созданного объекта. Этот адрес используется для инициализации указателя pint.Вторая форма оператора new выделяет память под массив заданного размера, состоящий из элементов определенного типа:int *pia = new int[4]; В этом примере память выделяется под массив из четырех элементов типа int. Данная форма оператора new не позволяет инициализировать элементы массива. Обе формы оператора new возвращают одинаковый указатель, в нашем примере это указатель на целое. И pint, и pia объявлены совершенно одинаково, однако pint указывает на единственный объект типа int, а pia — на первый элемент массива из четырех объектов типа int. Когда динамический объект больше не нужен, мы должны яв- ным образом освободить отведенную под него память. Это делается с помощью оператора delete, имеющего, как и new, две формы — для единичного объекта, и для массива: // освобождение единичного объекта delete pint; // освобождение массива delete[] pia; Память будет расходоваться впустую, она окажется неиспользуемой, однако возвратить ее системе нельзя, поскольку на 98 нее нет указателя. Такое явление получило специальное название утечка памяти. Указатель this. Методы каждого класса имеют доступ к указателю под названием this, который ссылается на сам объект. Таким образом, когда вызывается какой-либо метод, значением указателя this становится адрес объекта, для которого этот метод вызван, то есть его можно использовать для получения доступа к данным объекта, на который он ссылается. Более практичным применением указателя this является возврат значений из методов и перегружаемых операций в вызывающую программу. Многие встречались с проблемой возврата объекта из функции по ссылке, потому что объект был локальный по отношению к возвращающей функции и, следовательно, уничтожался во время выхода из функции. Необходим более долговечный объект. Объект, чьим методом является данная функция, прочнее, нежели его собственные методы. Методы объекта создаются и уничтожаются при каждом вызове, в то время как сам объект может быть удален только извне (например, delete). Таким образом, будет лучше, если в объект включен метод, потому что в таком случае этот объект и возвращается в результате работы своего метода в вызывающую программу. Новый подход легко осуществляется с помощью указателя this. #include <iostream> using namespace std; class alpha { private: int data;public: alpha(){ } // конструктор без аргументов alpha(int d) // конструктор с одним аргументом { data = d; }void display() // вывести данные { cout << data; }alpha& operator = (alpha& a) // перегружаемаЯ операциЯ = { data = a.data; // не делаетсЯ автоматически cout << "\n‡апущен оператор присваиваниЯ"; return *this; // вернуть копию this alpha } }; int main() { alpha a1(37); alpha a2, a3; a3 = a2 = a1; // перегружаемый =, дважды cout << "\na2="; a2.display(); // вывести a2cout << "\na3="; a3.display(); // вывести a3 cout << endl; return 0; }Так как this является указателем на объект, чей метод выполняется, то *this — это и есть сам объект, и выражение возвращает его по ссылке. Следует отметить, что указатель this нельзя использовать в статических методах, так как они не ассоциированы с конкретным объектом.

 

 

31.Виртуальные функции Виртуальные функции — это функции, которые объявляются с использованием ключевого слова virtual в базовом классе и переопределяются в одном или нескольких производных классах. При этом прототипы функций в разных классах одинаковы. Если типы функций различны, то механизм виртуальности для них не включается. Если функции, объявленные виртуальными, отличаются только типом возвращаемого значения, это является ошибкой.

Особенность использования виртуальных функций состоит в том, что при вызове функции, объявленной виртуальной, через указатель на базовый тип, во время выполнения программы определяется, какая виртуальная функция будет вызвана, в зависимости от того, на объект какого класса будет указывать указатель. Таким образом, когда указателю базового класса присвоены адреса объектов различных производных классов, выполняются различные версии виртуальных функций. Вследствие запретов и различий между перегрузкой обычных функций и перегрузкой виртуальных функций для описания пере- определения последних используется термин «замещение». Если в производном классе функция не замещает виртуальную, так как она имеет другой прототип или не объявлена, то вызывается функция базового класса. Вызов виртуальной функции обычно реализуется не как прямой вызов по таблице виртуальных функций класса. Эта таблица создается компилятором во время компиляции, а связывание происходит во время выполнения. Такой подход называется поздним или динамическим связыванием. Выбор функций в обычном порядке, во время компиляции, называется ранним или статическим связыванием. Чистая виртуальная функция — это функция, после объявления которой добавлено выражение = 0. Введя в класс такую функ- цию, мы программно защищаем объекты базового класса от реализации. Знак равенства не имеет ничего общего с операцией присваивания. Конструкция = 0 сообщает компилятору, что функция будет чисто виртуальной (листинг 1). #include <iostream> using namespace std; class Base //базовый класс {public: virtual void show() = 0; }; //чистаЯ виртуальнаЯ //функциЯ class Derv1: public Base //порожденный класс 1 { public: void show() { cout << "Derv1\n"; } }; class Derv2: public Base //порожденный класс 2 {public: void show() { cout << "Derv2\n"; } }; int main() { // Base bad; //невозможно создать объект из абстрактного класса Base* arr[2]; //массив указателей на базовый класс //Base bv; // ошибка Derv1 dv1; //объект производного класса 1 Derv2 dv2; //объект производного класса 2 arr[0] = &dv1; //занести адрес dv1 в массив arr[1] = &dv2; //занести адрес dv2 в массив arr[0]‐>show(); //выполнить функцию show() arr[1]‐>show(); //над обоими объектами return 0; } Результат: Derv1 Derv2

.

 

32. Дружественные функции. Дружественные функции. Принцип инкапсуляции и ограничения доступа к данным запрещает функциям, не являющимся методами соответствующего класса, доступ к скрытым (private) или защищенным данным объекта. Политика этих принципов такова, что, если функция не является членом объекта, она не может пользоваться определенным рядом данных. Тем не менее, есть ситуации, когда такая жесткая дискриминация приводит к значительным неудобствам. Допустим, что необходимо, чтобы функция работала с объектами двух разных классов. Например, функция будет рассматривать объекты двух классов как аргументы и обрабатывать их скрытые данные. В такой ситуации спасет лишь friend-функция.Пример#include<iostream>using namespace std;class beta; //нужно для объявления frifuncclass alpha{private:int data;public:alpha(): data(3) { } //конструктор без аргументовfriend int frifunc(alpha, beta); //дружественная функция}; class beta {private: int data; public: beta(): data(7) { } //конструктор без аргументов friend int frifunc(alpha, beta); };//дружественная функция int frifunc(alpha a, beta b) //определение функции { return(a.data + b.data);} int main() {alpha aa; beta bb; cout << frifunc(aa, bb) << endl; //вызов функции return 0;} Важное отличие дружественных функций состоит в том, что для них не используется указатель this. При объявлении дружественной функции-оператора должны передаваться два аргумента для бинарных операций и один для унарных. Есть ситуации, в которых использование дружественных функций обязательно. Перегрузку операции умножения объекта класса на целое можно записать в виде функции-члена и в виде дружественной функции. В то время, как операцию умножения целое на объект класса можно определить только через дружественную функцию.

33. Статические функции. Статические функции-члены класса не получают указатель this, соответственно, эти функции не могут обращаться к нестатическим членам класса. К статическим членам класса статические функции-члены класса обращаются посредством операции точка или ->. Статическая функция-член класса не может быть вирту- альной. К статическим функциям-членам класса можно обращать- ся, даже если не создано ни одного объекта данного класса, нужно только использовать полное имя члена класса. Если функция func1() является статической функцией-членом класса A, то ее можно вызвать: A::func1(); #include<iostream> using namespace std; class gamma { private: static int total; //всего объектов класса //(только объявление) int id; //ID текущего объекта public: gamma() //конструктор без аргументов { total++; //добавить объект id = total; } //id равен текущему значению total ~gamma() //деструктор { total‐‐; cout << "“даление ID " << id << endl; } static void showtotal() // статическаЯ функциЯ!!!!!!! { cout << "‚сего: " << total << endl; } void showid() // ЌестатическаЯ функциЯ { cout << "ID: " << id << endl; } }; int gamma::total = 0; // определение total int main() { gamma g1; gamma::showtotal();//без static обращение к методу только через объект gamma g2, g3; gamma::showtotal(); g1.showid(); g2.showid(); g3.showid(); cout << "‐‐‐‐‐‐‐‐‐‐конец программы‐‐‐‐‐‐‐‐‐‐\n"; return 0; }

 

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2017-10-12 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: