Поделиться через


Изменения в последовательности инициализации конструктора

Последовательность инициализации конструкторов класса в Visual C++ изменена по сравнению с управляемыми расширениями для C++.

Сравнение последовательности инициализации конструкторов

В управляемых расширениях для C++ инициализация конструкторов выполнялась в следующем порядке:

  1. Вызывается конструктор базового класса, если имеется.

  2. Выполняется оценка списка инициализации класса.

  3. Выполняется тело кода конструктора класса.

Этот порядок выполнения следует тем же соглашениям, что приняты для программирования машинного кода C++. В новом языке Visual C++ предписывается следующая последовательность выполнения для классов среды CLR:

  1. Выполняется оценка списка инициализации класса.

  2. Вызывается конструктор базового класса, если имеется.

  3. Выполняется тело кода конструктора класса.

Обратите внимание, что это изменение затронуло только классы среды CLR; в отношении собственных классов в Visual C++ действуют прежние соглашения. В обоих случаях эти правила действуют каскадно снизу вверх по всей иерархической цепочке некоторого заданного класса.

Рассмотрим следующий пример кода, в котором использованы управляемые расширения для C++:

__gc class A
{
public:
   A() : _n(1)
   {
   }

protected:
   int _n;
};

__gc class B : public A
{
public:
   B() : _m(_n)
   {
   }
private:
   int _m;
};

В соответствии с описанной выше последовательностью инициализации конструкторов, должен соблюдаться следующий порядок выполнения при создании новых экземпляров класса B:

  1. Вызывается конструктор базового класса A. Элемент _n инициализируется значением 1.

  2. Выполняется оценка списка инициализации класса B. Элемент _m инициализируется значением 1.

  3. Выполняется тело кода конструктора класса B.

А теперь рассмотрим тот же код в синтаксисе нового языка Visual C++:

ref class A
{
public:
   A() : _n(1)
   {
   }

protected:
   int _n;
};

ref class B : A
{
public:
   B() : _m(_n)
   {
   }
private:
   int _m;
};

Порядок выполнения при создании новых экземпляров класса B в новом синтаксисе следующий:

  1. Выполняется оценка списка инициализации класса B. Элемент _m инициализируется в 0 (0 является инициализированным значением члена класса _m).

  2. Вызывается конструктор базового класса A. Элемент _n инициализируется значением 1.

  3. Выполняется тело кода конструктора класса B.

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

Что означает переход от управляемых расширений для C++ к Visual C++ 2010

Во многих случаях изменения в последовательности выполнения конструкторов класса должны быть понятны для программиста, поскольку для базовых классов нет определения поведения унаследованных классов. Тем не менее, как видно из этих примеров кода, конструкторы унаследованных классов могут в значительной степени измениться, если их списки инициализации зависят от значений членов базового класса. При преобразовании кода из управляемых расширений для C++ в новый синтаксис следует изучить возможность переноса таких конструкций в тело конструктора класса, выполнение которого гарантированно происходит в последнюю очередь.

См. также

Ссылки

Конструкторы (C++)

Инициализаторы конструкторов

Основные понятия

Управляемые типы (C++/CL)