Динамическое создание объектов

Для динамического создания объектов в языке C++ предназначен оператор new. Оператор new выделяет объем памяти, необходимый для хранения объекта, и возвращает его адрес. Работать в дальнейшем с объектом можно с помощью указателя. Существуют три способа динамического создания объекта:

<Указатель> = new <Название класса>;
<Указатель> = new <Название класса>(<Значения для конструктора>);
<Указатель> = new <Название класса>[<Количество объектов>];

В первом способе вызывается конструктор без параметров. Во втором способе внутри круглых скобок можно передать значения конструктору класса. Третий способ позволяет создать массив объектов. Обратите внимание на то, что в этом случае нельзя передать значения конструктору, поэтому необходимо предусмотреть конструктор без параметров. Примеры:

A *pA = new A;          // Способ 1
B *pB = new B(20);      // Способ 2
C *pC = new C[2];       // Способ 3

Чтобы обратиться к членам класса с помощью указателя при использовании способов 1 и 2 следует вместо точечной нотации применять оператор ->. В третье способе необходимо внутри квадратных скобок указать индекс элемента, а затем с помощью точечной нотации обратиться к члену класса. Примеры:

pA->x = 100;            // Для способа 1
pB->x = 100;            // Для способа 2
pC[0].x = 100;          // Для способа 3
pC[1].x = 200;          // Для способа 3

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

delete <Указатель>;     // Для способов 1 и 2
delete [] <Указатель>;  // Для способа 3

Пример:

delete pA;
delete pB;
delete [] pC;

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

pA = nullptr;
pB = nullptr;
pC = nullptr;

При выделении памяти может возникнуть ситуация нехватки памяти. В случае ошибки оператор new возбуждает исключение bad_alloc (класс исключения объявлен в файле new). Обработать это исключение можно с помощью конструкции try...catch. Пример выделения памяти с обработкой исключения:

#include <new>
// ... Фрагмент опущен ...
A *pA = nullptr;  // Создаем указатель
try {
   pA = new A;    // Выделяем память
}
catch (std::bad_alloc &ex) {
   // Обработка исключения
}

Выделение памяти производится внутри блока try. Если при этом возникнет исключение bad_alloc, то управление будет передано в блок catch. После выполнения инструкций в блоке catch управление передается инструкции, расположенной сразу после блока. Иными словами, считается, что вы обработали исключение и можно продолжить выполнение программы. Следует учитывать, что пользоваться указателем после обработки нельзя, поэтому внутри блока catch обычно выводят сообщение об ошибке и завершают выполнение программы. Если исключение не обработать, то программа аварийно завершится. Если исключение не возникло, то инструкции внутри блока catch не выполняются.

Обратите внимание на то, что объявление указателя производится вне блока try. Если объявление разместить внутри блока, то область видимости переменной будет ограничена этим блоком. После выхода из блока переменная автоматически уничтожается, а выделенная память операционной системе не возвращается. Поэтому, объявление указателя должно находиться перед блоком, а не внутри него.

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

Листинг 13.19. Динамическое создание объектов

#include <iostream>
#include <new>

class A {
public:
   int x;
   A() { x = 0; std::cout << "A::A()" << std::endl; }
   ~A() { std::cout << "A::~A()" << std::endl; }
};
class B {
public:
   int y;
   B(int a) { y = a; std::cout << "B::B()" << std::endl; }
   ~B() { std::cout << "B::~B()" << std::endl; }
};

int main() {
   A *pA = nullptr;
   B *pB = nullptr;
   try { pA = new A; }     // Способ 1
   catch (std::bad_alloc &ex) {
      std::cout << "Error" << std::endl;
      return 1;
   }
   pA->x = 100;
   std::cout << pA->x << std::endl; // 100
   delete pA;
   pA = nullptr;
   try { pB = new B(20); } // Способ 2
   catch (std::bad_alloc &ex) {
      std::cout << "Error" << std::endl;
      return 1;
   }
   std::cout << pB->y << std::endl; // 20
   delete pB;
   pB = nullptr;
   try { pA = new A[2]; }  // Способ 3
   catch (std::bad_alloc &ex) {
      std::cout << "Error" << std::endl;
      return 1;
   }
   pA[0].x = 100;
   pA[1].x = 200;
   std::cout << pA[0].x << std::endl; // 100
   std::cout << pA[1].x << std::endl; // 200
   delete [] pA;
   pA = nullptr;
   return 0;
}
Примечание

Учебник C++ (MinGW-W64)
Учебник C++ (MinGW-W64) в формате PDF

Помощь сайту

ЮMoney (Yandex-деньги): 410011140483022

ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов