Создание и уничтожение объектов. Конструкторы и деструкторы. Виды конструкторов. Способы создания объектов. - Painted-Black/BMSTU-OOP GitHub Wiki

Как правило, удобнее инициализировать поля объекта автоматически в момент его создания, а не явно вызывать в программе соответствующий метод. Такой способ инициализации реализуется с помощью особого метода класса, называемого конструктором. Конструктор — это метод класса, выполняющийся автоматически в момент создания объекта.

У конструкторов есть несколько особенностей, отличающих их от других мето- дов класса. Во-первых, имя конструктора в точности совпадает с именем класса (в нашем примере таким именем является Counter). Таким образом, компилятор отличает конструкторы от других методов класса. Во-вторых, у конструкторов не существует возвращаемого значения. Это объясняется тем, что конструктор автоматически вызывается системой, и, следовательно, не существует вызывающей программы или функции, которой конструктор мог бы возвратить значение. Следо- вательно, указание возвращаемого значения для конструктора не имеет смысла. Отсутствие типа возвращаемого значения у конструкторов является вторым при- знаком, по которому компилятор может отличить их от других методов класса.

Одной из наиболее часто возлагаемых на конструктор задач является инициа- лизация полей объекта класса.

Counter() 
    { count = 0; } 

Такая форма записи не рекомендуется, несмотря на то, что она не содержит ошибок. Инициализация в нашем примере происходит следующим образом:

Counter() : count(0)   
    {  } 

Инициализация расположена между прототипом метода и телом функции и предварена двоеточием. Инициализирующее значение помещено в скобках после имени поля. Если необходимо инициализировать сразу несколько полей класса, то значе- ния разделяются запятыми, и в результате образуется список инициализации:

SomeClass() : m1(7), m2(33), m3(4)    
    {  }

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

Раздел инициализации также служит для вызова конструкторов базовых классов. Порядок в разделе инициализации не влияет на порядок вызова конструкторов.

Конструктор не может быть константным, volatile, virtual, статичным. Не наследуется. Проблема преобразования типов – не даёт нормально обработать объект при преобразовании его из одного типа в другой. Может возникнуть ситуация, что при создании объекта возникнет ошибка, и будет неясно создан объект или нет. Чтобы не гадать о существовании объекта, используют оператор explicit – он запрещает преобразование типов и неявный вызов конструктора.

В ЛЮБОМ классе автоматически создаются два конструктора – конструктор по-умолчанию (не принимающий параметра) и конструктор копирования (вызывается при передаче и возврате по значению). Однако если в классе описан хотя бы один не стандартный конструктор, то конструктор-по-умолчанию не создаётся. Аналогично, можно явно задавать конструктор копирования, в случае если под объект так или иначе выделяются ресурсы. Деструктор также необходимо объявлять явно. Если не нужна возможность передачи\возврата по значению (чтобы не создавались копии), то конструктор необходимо объявить приватным.

  • int a; // можно инициализировать в разделе инициализации и в теле конструктора

  • Объекты инициализируются только в списке параметров

  • static int as; // можно работать в теле конструктора. Инициализация производится до создания хотя бы одного объекта данного класса. До C11 - вне класса:

      class A
      {
      ...
      };
    

    С C11 - можно инициализировать при объявлении класса.

  • const int ac; // инициализировать в разделе инициализации.

  • const static int asc; // инициализация только в классе.

Способы создания объекта

class MyComplex
{
private:
    double xe, im;
public:
    Complex() : Complex(0, 0) {}
    [explicit] Complex(double z) : Complex(z, 0) {} // explicit запрещает неявный вызов конструктора
    Complex(double z, double i() : Complex(z, i) {}
    Complex(const Complex &c);
};

...

Complex f(); // функиця
Complex a; // определение объекта, 1-й конструктор
Complex b1 = Complex(1); // 2-й конструктор
Complex b2(1); // 2-й конструктор
Complex b3 = 3; // 2-й конструктор
Complex b4 = {4}; // 2-й конструктор
Complex b5{5}; // 2-й конструктор
Complex c1 = Complex(1, 2);
Complex c2(3, 4);
Complex c3 = {5, 6};
Complex c4{7, 8};
Complex d1 = Complex(c1);
Complex d2(c2);
Complex d3 = c3;

Существует функция, автоматически вызываемая при уничтожении объекта и называемая деструктором.

Деструктор имеет имя, совпадающее с именем конструктора (а следовательно, и класса) и предваряющееся символом ~:

class Foo 
{
private:
    int data;   
public:     
    Foo() : data(0)  // конструктор (имя такое же, как у класса)        
        { }    
    ~Foo() // деструктор (то же имя, но с символом ~)        
        { } 
};

Подобно конструкторам, деструкторы не возвращают значения и не имеют аргументов, поскольку невозможно уничтожение объектов несколькими способами. Наиболее распространенное применение деструкторов — освобождение памяти, выделенной конструктором при создании объекта. Также на деструкторы возлагается функция разрыва связей.

Конструкторы НЕ МОГУТ быть константными или статически, могут быть виртуальными.

В C11 деструктор сново стало можно вызывать явно.

Деструкторы базового класса обязательно должны быть виртуальными? Допустим, чтобы удалить объект порожденного класса, вы выполнили delete над указателем базового класса, указывающим на порожденный класс. Если деструктор базового класса не является виртуальным, тогда delete, будучи обычным методом, вызовет деструктор для базового класса вместо того, чтобы запустить деструктор для порожденного класса. Это приведет к тому, что будет удалена только та часть объекта, которая относится к базовому классу.