Глава 8
В динамической памяти можно размещать объекты любых классов. Например
если объявить класс Cat , то для манипулирования этим объектом можно
создать указатель, в котором будет храниться его адрес.
Cat*pCat = new Cat;
В данном случае в операторе new использован конструктор класса
по умолчанию, то есть без параметров. Следует помнить что при создании
объекта класса конструктор вызывается всегда, не зависимо от того
размещается объект в стеке или в области динамической памяти.
Вверх
Создание объекта в области динамической памяти использовав
// Listing 8.5
class SimpleCat{
public:
SimpleCat::SimpleCat(){
SimpleCat::~SimpleCat(){
int main(){
cout << "SimpleCat Frisky...\n";
cout << "SimpleCat *pRags = new SimpleCat...\n";
cout << "delete pRags...\n";
cout << "Exiting, watch Frisky go...\n";
}
1.SimpleCat Frisky...
Краткое содержание программы:
Мы видим класс в котором прототипы функций конструктора и деструктора
Теперь можно было бы инициализировать указатель новым местом в динамической
Проверь это на практике!
Вверх
Для доступа к переменным-членам и функциям-членам, созданным в динамической
// Listing 8.6
#include <iostream>
class SimpleCat{
public:
int main(){
cout << "Frisky is " << Frisky->GetAge() << " years old\n";
}
Результат:
Здесь представлен тот же класс SimpleCat в нем добавлена функция
Посмотрим главную программу:
Сначала в динамической памяти создается объект -экземпляр класса SimpleCat.
Далее вызывается функция GetAge() которая позволяет удостовериться что
Поскольку речь идет об указателе то для обращения к данным-членам
Далее при помощи функции SetAge() устанавливается новое значение
Затем вновь вызывается функция позволяющая посмотреть как изменился
Вверх
Указателем на объект размещенный в динамической памяти, может быть
Таким образом эти переменные будут попадать в динамическую память
Посмотрите пример:
#include <iostream>
class SimpleCat
{
public:
int GetWeight() const {return *itsWeight;}
}
SimpleCat::SimpleCat(){
SimpleCat::~SimpleCat(){
int main(){
Frisky->SetAge(5);
std::cout << "Frisky is " << Frisky->GetAge() << " years old\n";
return 0;
}
Результат:
Анализ:
В составе объявленного класса SimpleCat находятся две переменные-члена,
Конструктор создает в динамической памяти обе переменные и инициализирует их
Обратите внимание на псевдоконструктор который не только выделяет
Деструктор освобождает выделенную память
Посмотрим главную программу.
Frisky->SetAge(5);
std::cout << "Frisky is " << Frisky->GetAge() << " years old\n";
При уничтожении класса Frisky вызывается деструктор класса SimpleCat.
В реальной программе это выглядело бы довольно глупо обращаться
Необходимо четко ставить задачу , которую предстоит решить.
Допустим необходимо создать класс, членом которого будет объект другого
Вверх
Часто задаваемые вопросы:
Если в стеке объявлен объект, который имеет переменные -члены, размещенные
Пример:
class SimpleCat{
public:
private: }
SimpleCat::SimpleCat(){
SimpleCat::~SimpleCat(){
int main()
{
Frisky.SetAge(5);
return 0;
}
В стеке находится локальный объект Frisky. Этот объект обладает двумя
Вверх
Каждая функция класса имеет скрытый указатель this.
// Listing 8.8
class Rectangle{
public:
void SetLength(int length)
int GetLength() const
void SetWidth(int width)
int GetWidth() const
private:
};
Rectangle::Rectangle(){
Rectangle::~Rectangle()
int main(){
theRect.SetLength(20);
cout << "theRect is " << theRect.GetLength()
Функции доступа SetLenth() и GetLenth() используют указатель this
Необходимо усвоить что каждый объект обладает скрытым указателем
О создании и удалении указателя this позаботится сам компилятор.
Назад |
Начало урока |
Вверх |
Вперед
Удаление объектов
оператор new и удаление объекта
// Creating objects on the free store
// using new and delete
#include <iostream.h>
~SimpleCat();
itsAge = 1;
}
SimpleCat Frisky;
SimpleCat * pRags = new SimpleCat;
delete pRags;
return 0;
Результат:
2.Constructor called.
3.SimpleCat *pRags = new SimpleCat...
4.Constructor called.
5.delete pRags...
6.Destructor called.
7.Exiting, watch Frisky go...
8.Destructor called.
Сначала создается объект Frisky типа SimpleCat в области стека.
Затем при помощи оператора new создается такой же объект правда
без имени в динамической памяти и на него указывает указатель
pRags .
Затем этот объект удаляется из динамической памяти а при выходе из
программы удаляется и объект из стека.
а также одна закрытая переменная обозначающая возраст кота.
Посмотрим главную программу:
cout << "SimpleCat Frisky...\n";
SimpleCat Frisky;
на него указывает указатель pRags. Как мы видим указатель указывает
на созданный в динамической памяти экземпляр класса SimpleCat
но у самого этого экземпляра нет имени. Как было имя при создании
экземпляра в стеке (Frisky). Очевидно в имени здесь нет необходимости.
(можно имя экземпляра созданного в динамической памяти определить так:
"экземпляр класса SimpleCat созданный в динамической памяти на который
указывает указатель pRags" Вот такое длинное имя)
cout << "SimpleCat *pRags = new SimpleCat...\n";
SimpleCat * pRags = new SimpleCat;
delete, (освобождается и место в динамической памяти)
при этом вызывается деструктор класса.
cout << "delete pRags...\n";
delete pRags;
памяти например так(или новым объектом):
pRags = new SimpleCat;
cout << "Exiting, watch Frisky go...\n";
из области действия по завершению функции main() и освобождается стек.
Доступ к переменным членам
расположенным в динамической памяти
памяти, необходимо использовать ссылку на указатель и точечный оператор.
(*pRags).GetAge();Круглые скобки используются для того чтобы сначала
обратиться к значению по адресу в указателе, а только затем к его
функции GetAge().
или лучше использовать оператор косвенного доступа "указатель на" [->].
что то же самое но более популярно.
// Accessing data members of objects on the heap
// using the -> operator
~SimpleCat()
int GetAge() const {return itsAge;}
void SetAge(int age) {itsAge = age;}
Frisky->SetAge(5);
cout << "Frisky is " << Frisky->GetAge() << " years old\n";
delete Frisky;
return 0;
Frisky is 2 years old
Frisky is 5 years old
GetAge() позволяющая увидеть закрытую переменную itsAge и
функция SetAge() позволяющая извне изменить значение закрытой переменной
itsAge.
Заданный по умолчанию конструктор устанавливает его возраст равным 2.
SimpleCat * Frisky = new SimpleCat;
конструктор сработал правильно.
cout << "Frisky is " << Frisky->GetAge() << " years old\n";
и функциям-членам объекта на который он указывает,
используется оператор косвенного доступа ( -> )
возраста опять же используется оператор косвенного доступа и указатель.
Frisky->SetAge(5);
возраст кота.
cout << "Frisky is " << Frisky->GetAge() << " years old\n";
Далее освобождается динамическая память и указатель на нее.
delete Frisky;
Данные-члены в динамической памяти
один или несколько переменных членов класса. Операцию по выделению
динамической памяти для переменных можно поместить в конструктор
класса или в одну из его функций, а освобождать динамическую память
и указатель можно в деструкторе.
автоматически в момент объявления объекта и освобождать ее
при удалении объекта.
Указатели как члены класса.
// Listing 8.7
// Pointers as data members
// accessed with -> operator
~SimpleCat();
int GetAge() const {return *itsAge;}
void SetAge(int age) {*itsAge = age;}
void setWeight (int weight) {*itsWeight = weight;}
int * itsWeight;
itsWeight = new int(5);
}
delete itsWeight;
}
std::cout << "Frisky is " << Frisky->GetAge() << " years old\n";
delete Frisky;
Frisky is 2 years old
Frisky is 5 years old
являющиеся указателями на тип int
private:
int * itsWeight;
начальными значениями.
SimpleCat::SimpleCat(){
itsWeight = new int(5);
}
в динамической памяти место для переменных, но и инициализирует их
начальными значениями переменную itsAge -значением 2, переменную
itsWeight-значением 5.
SimpleCat::~SimpleCat(){
delete itsWeight;
}
поскольку теперь они станут недоступны. Это именно тот случай когда
действует исключение из правил.
Вызывающей функции main() абсолютно безразлично что переменные itsAge
itsWeight являются указателями на области в динамической памяти.
Функция main() просто вызывает функции GetAge() и SetAge(),
а подробности управления памятью инкапсулированы в реализации класса,
как и должно быть.
std::cout << "Frisky is " << Frisky->GetAge() << " years old\n";
delete Frisky;
Деструктор удаляет каждый из указателей принадлежащих объекту.
Если они в свою очередь указывают на объекты других классов,
то будут вызваны так же и их деструкторы.
к своим переменным по ссылкам, если только у объекта нет достаточно
серьезных оснований для этого.
Какие реальные основания у объекта обращаться к своим переменным по ссылкам?
класса, причем второй объект может создаваться еще до возникновения
первого и существовать после его удаления. В этом случае доступ ко
второму объекту должен осуществляться только по ссылке, то есть
с помощью указателя.
Допустим первым объектом является окно, а вторым-документ. Вполне понятно
что окно должно иметь доступ к документу. С другой стороны
продолжительность существования документа никак не контролируется окном.
Поэтому для окна важно хранить лишь ссылку на этот документ.
Об использовании ссылок в уроке 9.
в динамической памяти, что будет находиться в стеке, а что в динамической
памяти?
#include <iostream>
~SimpleCat();
int GetAge() const {return *itsAge;}
//другие функции
int * itsWeight;
itsWeight = new int(5);
}
delete itsWeight;
}
cout << "Frisky is " << Frisky.GetAge() << " years old\n";
cout << "Frisky is " << Frisky.GetAge() << " years old\n";
указателями, каждый из которых занимает по четыре байта пространства
стека, (это как обычно)и содержит адрес целого числа, находящегося в
динамической памяти. Таким образом восемь байт находятся в стеке, и восемь
байт в динамической памяти.
в динамичкеской памяти.
cout << "Frisky is " << Frisky->GetAge() << " years old\n";
Указатель this
Этот указатель содержит адрес текущего объекта. Следовательно при каждом
обращении к функциям GetAge() и SetAge() указатель объекта this
включается как скрытый параметр.
Пример явного использования указателя this приведен в листинге 8.8
// Using the this pointer
#include <iostream.h>
~Rectangle();
{ this->itsLength = length;}
{ return this->itsLength;}
{ itsWidth = width;}
{ return itsWidth;}
int itsWidth;
itsLength = 10;
}
cout << "theRect is " << theRect.GetLength()
<< " feet long.\n";
cout << "theRect is " << theRect.GetWidth()
<< " feet wide.\n";
theRect.SetWidth(10);
<< " feet long.\n";
cout << "theRect is " << theRect.GetWidth()
<< " feet wide.\n";
return 0;
для обеспечения доступа к переменным-членам объекта Rectangle
в явном виде, а функции доступа SetWeigth() и GetWeigth()
организованы традиционным способом. Как видите они ведут себя
одинаково, хотя синтаксис последних проще.
this является указателем, он содержит адрес текущего объекта
и в этой роли может оказаться достаточно мощным инструментом.
this содержащим адрес самого объекта.(подробнее глава 10)
Содержание