Интернет, компьютеры, софт и прочий Hi-Tech

Подписаться через RSS2Email.ru

Страница 10 из 10 - Предыдущая - Следующая
Все страницы - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10

Краткий FAQ по C++

[11.9] А могу ли я явно вызывать деструктор для объекта, созданного при помощи new?

Скорее всего, нет.

За исключением того случая, когда вы использовали синтаксис размещения для оператора new [11.10], вам следует просто удалять объекты при помощи delete, а не вызывать явно деструктор. Предположим, что вы создали объект при помощи обычного new:

Fred* p = new Fred();

В таком случае деструктор Fred::~Fred() будет автомагически вызван, когда вы удаляете объект:

delete p; // Вызывает p->~Fred()

Вам не следует явно вызывать деструктор, поскольку этим вы не освобождаете память, выделенную для объекта Fred. Помните: delete p делает сразу две вещи [16.8]: вызывает деструктор и освобождает память.

[11.10] Что такое "синтаксис размещения" new ("placement new") и зачем он нужен?

Есть много случаев для использования синтаксиса размещения для new. Самое простое - вы можете использовать синтаксис размещения для помещения объекта в определенное место в памяти. Для этого вы указываете место, передавая указатель на него в оператор new:

    #include <new>  // Необходимо для использования синтаксиса размещения
    #include "Fred.h"     // Определение класса Fred

    void someCode()
    {
      char memory[sizeof(Fred)];     // #1
      void* place = memory;          // #2

      Fred* f = new(place) Fred();   // #3 (смотрите "ОПАСНОСТЬ" ниже)
      // Указатели f и place будут равны

      // ...
    }

В строчке #1 создаётся массив из sizeof(Fred) байт, размер которого достаточен для хранения объекта Fred. В строчке #2 создаётся указатель place, который указывает на первый байт массива (опытные программисты на С наверняка заметят, что можно было и не создавать этот указатель; мы это сделали лишь чтобы код был более понятным [As if - :) YM]). В строчке #3 фактически происходит только вызов конструктора Fred::Fred(). Указатель this в конструкторе Fred будет равен указателю place. Таким образом, возвращаемый указатель тоже будет равен place.

СОВЕТ: Не используйте синтаксис размещения new, за исключением тех случаев, когда вам действительно нужно, чтобы объект был размещён в определённом месте в памяти. Например, если у вас есть аппаратный таймер, отображённый на определённый участок памяти, то вам может понадобиться поместить объект Clock по этому адресу.

ОПАСНО: Используя синтаксис размещения new вы берёте на себя всю ответственность за то, что передаваемый вами указатель указывает на достаточный для хранения объекта участок памяти с тем выравниванием (alignment), которое необходимо для вашего объекта. Ни компилятор, ни библиотека не будут проверять корректность ваших действий в этом случае. Если ваш класс Fred должен быть выровнен четырёхбайтовой границе, но вы передали в new указатель на не выровненный участок памяти, у вас могут быть большие неприятности (если вы не знаете, что такое "выравнивание" (alignment), пожалуйста, не используйте синтаксис размещения new). Мы вас предупредили.

Также на вас ложится вся ответственность по уничтожения размещённого объекта. Для этого вам необходимо явно вызвать деструктор:

    void someCode()
    {
      char memory[sizeof(Fred)];
      void* p = memory;
      Fred* f = new(p) Fred();
      // ...
      f->~Fred();   // Явный вызов деструктора для размещённого объекта
    }

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

[11.11] Когда я пишу деструктор, должен ли я явно вызывать деструкторы для объектов-членов моего класса?

Нет. Никогда не надо явно вызывать деструктор (за исключением случая с синтаксисом размещения new [11.10]).

Деструктор класса (неявный, созданный компилятором, или явно описанный вами) автоматически вызывает деструкторы объектов-членов класса. Эти объекты уничтожаются в порядке обратном порядку их объявления в теле класса:

    class Member {
    public:
      ~Member();
      // ...
    };

    class Fred {
    public:
      ~Fred();
      // ...
    private:
      Member x_;
      Member y_;
      Member z_;
    };

    Fred::~Fred()
    {
      // Компилятор автоматически вызывает z_.~Member()
      // Компилятор автоматически вызывает y_.~Member()
      // Компилятор автоматически вызывает x_.~Member()
    }

[11.12] Когда я пишу деструктор производного класса, нужно ли мне явно вызывать деструктор предка?

Нет. Никогда не надо явно вызывать деструктор (за исключением случая с синтаксисом размещения new [11.10]).

Деструктор производного класса (неявный, созданный компилятором, или явно описанный вами) автоматически вызывает деструкторы предков. Предки уничтожаются после уничтожения объектов-членов производного класса. В случае множественного наследования непосредственные предки класса уничтожаются в порядке обратном порядку их появления в списке наследования.

    class Member {
    public:
      ~Member();
      // ...
    };

    class Base {
    public:
      virtual ~Base();     // Виртуальный деструктор[20.4]
      // ...
    };

    class Derived : public Base {
    public:
      ~Derived();
      // ...
    private:
      Member x_;
    };

    Derived::~Derived()
    {
      // Компилятор автоматически вызывает x_.~Member()
      // Компилятор автоматически вызывает Base::~Base()
    }

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

Страница 10 из 10 - Предыдущая - Следующая

Биржа долевых инвестиций SIMEX.

Последнее редактирование: 2009-10-02 17:36:22

Метки материала: C++, FAQ, программирование, деструкторы, объект, new, delete, деструктор, освобождение памяти, синтаксис размещения, placement new, указатель, выделение памяти, класс, объекты, class, производный класс, множественное наследование, компилятор

Оставьте, пожалуйста, свой комментарий к публикации

Представиться как     Антибот:
   

Просьба не постить мусор. Если вы хотите потестить xBB, воспользуйтесь кнопкой предварительного просмотра на панели инструментов xBBEditor-а.


© 2007-2019, Дмитрий Скоробогатов.
Разрешается воспроизводить, распространять и/или изменять материалы сайта
в соответствии с условиями GNU Free Documentation License,
версии 1.2 или любой более поздней версии, опубликованной FSF,
если только иное не указано в самих материалах.