Специальный блок инструкций вызываемый при создании объекта это

У этого термина существуют и другие значения, см. Конструктор.

В объектно-ориентированном программировании конструктор класса (от англ. constructor, иногда сокращают ctor) — специальный блок инструкций, вызываемый при создании объекта.

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

Термин «конструктор» также используется для обозначения одного из тегов, описывающих данные в алгебраическом типе данных. Это использование несколько отличается от описываемого в статье. Для дополнительной информации смотрите Алгебраический тип данных.

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

Содержание

  • 1 Назначение конструктора
  • 2 Виды конструкторов
    • 2.1 Конструктор по умолчанию
    • 2.2 Конструктор копирования
    • 2.3 Конструктор преобразования
    • 2.4 Виртуальный конструктор
  • 3 Синтаксис
    • 3.1 С++
      • 3.1.1 Пример
    • 3.2 Python
      • 3.2.1 Пример
    • 3.3 Delphi
      • 3.3.1 Пример
    • 3.4 Java
      • 3.4.1 Пример
    • 3.5 JavaScript
      • 3.5.1 Пример
    • 3.6 Visual Basic .NET
      • 3.6.1 Пример
    • 3.7 C#
      • 3.7.1 Пример
    • 3.8 Эйфель
      • 3.8.1 Пример
    • 3.9 ColdFusion
      • 3.9.1 Пример
    • 3.10 PHP
      • 3.10.1 Пример
    • 3.11 Perl
      • 3.11.1 Пример
  • 4 Упрощенные конструкторы (с псевдокодом)
  • 5 Примечания
  • 6 Ссылки
  • 7 См. также

Назначение конструктора

Одна из ключевых особенностей ООП — инкапсуляция: внутренние поля объекта напрямую недоступны, и пользователь может работать с объектом только как с единым целым, через открытые (public) методы. Каждый метод, в идеале, должен быть устроен так, чтобы объект, находящийся в «допустимом» состоянии (то есть когда выполняется инвариант класса), после вызова метода также оказался в допустимом состоянии. И первая задача конструктора — перевести поля объекта в такое состояние.

Вторая задача — упростить пользование объектом. Объект — не «вещь в себе», ему часто приходится требовать какую-то информацию от других объектов: например, объект File, создаваясь, должен получить имя файла. Это можно сделать и через метод:

  File file;
  file.open("in.txt", File::omRead);

Но удобнее открытие файла сделать в конструкторе:[1]

  File file("in.txt", File::omRead);

Виды конструкторов

Некоторые языки программирования различают несколько особых типов конструкторов:

  • конструктор по умолчанию — конструктор, не принимающий аргументов;
  • конструктор копирования — конструктор, принимающий в качестве аргумента объект того же класса (или ссылку из него);
  • конструктор преобразования — конструктор, принимающий один аргумент (эти конструкторы могут вызываться автоматически для преобразования значений других типов в объекты данного класса).
class Complex
{
 public:
  // Конструктор по умолчанию 
  // (в данном случае является также и конструктором преобразования)
  Complex(double i_re = 0, double i_im = 0)
      : re(i_re), im(i_im)
  {}
 
  // Конструктор копирования
  Complex(const Complex &obj)
  {
   re = obj.re;
   im = obj.im;
  }
  private:
    double re, im;
};

Конструктор по умолчанию

Конструктор не имеющий обязательных аргументов. Используется при создании массивов объектов, вызываясь для создания каждого экземпляра. В отсутствие явно заданного конструктора по умолчанию его код генерируется компилятором (что на исходном тексте, естественно, не отражается).

Конструктор копирования

Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функции по значению.

Конструктор копирования в основном необходим, когда объект имеет указатели на объекты выделенные в куче. Если программист не создаёт конструктор копирования, то компилятор создаст неявный конструктор копирования, который копирует указатели как есть, то есть фактическое копирование данных не происходит и два объекта ссылаются на одни и те же данные в куче. Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе.

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

Конструктор преобразования

Конструктор, принимающий один аргумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально.

Виртуальный конструктор

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

«Виртуальными конструкторами» называют похожий, но другой механизм, присутствующий в некоторых языках — например, он есть в Delphi, но нет в C++ и Java. Этот механизм позволяет создать объект любого заранее неизвестного класса при двух условиях:

  • этот класс является потомком некоего наперёд заданного класса (в данном примере это класс TVehicle);
  • на всём пути наследования от базового класса к создаваемому цепочка переопределения не обрывалась. При переопределении виртуального метода синтаксис Delphi требует ключевое слово overload, чтобы старая и новая функции с разными сигнатурами могли сосуществовать, override для переопределения функции либо reintroduce для задания новой функции с тем же именем — последнее недопустимо.
type
  TVehicle = class
      constructor Create;  virtual;
    end;
 
  TAutomobile = class (TVehicle)
      constructor Create;  override;
    end;
 
  TMotorcycle = class (TVehicle)
      constructor Create;  override;
    end;
 
  TMoped = class (TMotorcycle)  // обрываем цепочку переопределения - заводим новый Create
      constructor Create(x : integer);  reintroduce;
    end;

В языке вводится так называемый классовый тип (метакласс). Этот тип в качестве значения может принимать название любого класса, производного от TVehicle.

type
  CVehicle = class of TVehicle;

Такой механизм позволяет создавать объекты любого заранее неизвестного класса, производного от TVehicle.

var
  cv : CVehicle;
  v : TVehicle;
 
cv := TAutomobile;
v := cv.Create;

Заметьте, что код

cv := TMoped;
v := cv.Create;

является некорректным — директива reintroduce разорвала цепочку переопределения виртуального метода, и в действительности будет вызван конструктор TMotorcycle.Create (а значит, будет создан мотоцикл, а не мопед!)

См. также Фабрика (шаблон проектирования)

Синтаксис

С++

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

Пример

class ClassWithConstructor {
 public:
  /* Инициализация внутреннего объекта с помощью конструктора */
  ClassWithConstructor(float parameter): object(parameter) {}/* вызов конструктора AnotherClass(float); */
 private:
  AnotherClass object;
};

Python

В языке Python конструктором является метод класса с именем __init__ . Кроме того не следует забывать, что первым аргументом любого метода должен быть указатель на контекст класса self.

Пример

class ClassWithConstructor:
 
    def __init__(self):
        """This method is constructor."""
        pass

Delphi

В Delphi, в отличие от C++, для объявления конструктора служит ключевое слово constructor. Имя конструктора может быть любым, но рекомендуется называть конструктор Create.

Пример

  TClassWithConstructor = class
    public
    constructor Create;
  end;

Java

Некоторые отличия между конструкторами и другими методами Java:

  • конструкторы не имеют чётко определённого типа возвращаемых данных;
  • конструкторы не могут напрямую вызываться (необходимо использовать ключевое слово new);
  • конструкторы не могут быть synchronized, final, abstract, native и static типов;
  • конструкторы всегда выполняются в том же потоке.

Пример

public class Example{
  // Конструктор по умолчанию
  public Example(){
    this(1);
  }
 
  // Перегрузка конструктора
  public Example(int input){
    data = input;
  }
 
  private int data;
}
// код, иллюстрирующий создание объекта описанным выше конструктором
Example e = new Example(42);

JavaScript

В JavaScript в качестве конструктора выступает обычная функция, используемая в качестве операнда оператора new. Для обращения к созданному объекту используется ключевое слово this.

Пример

function Example(initValue) {
    this.myValue = initValue;
}
 
Example.prototype.getMyValue = function() {
    return this.myValue; 
}
// код, иллюстрирующий создание объекта описанным выше конструктором
var exampleObject = new Example(120);

Visual Basic .NET

Конструкторы в Visual Basic .NET используют обычный метод объявления с именем New.

Пример

Class Foobar
  Private strData As String
 
  ' Constructor
  Public Sub New(ByVal someParam As String)
     strData = someParam
  End Sub
End Class
' некий код
' иллюстрирующий создание объекта описанным выше конструктором
Dim foo As New Foobar(".NET")

C#

Пример

class myClass
{
  private int mA;
  private string mB;
 
  public myClass(int a, string b)
  {
    mA = a;
    mB = b;
  }
}
// код, иллюстрирующий создание объекта описанным выше конструктором
myClass c = new myClass(42, "string");

Эйфель

В Эйфеле подпрограммы, которые инициализируют объекты, называются процедурами создания. Процедуры создания в чём-то подобны конструкторам и в чём-то отличаются. Они имеют следующие характеристики:

  • Процедуры создания не имеют никакого явного типа результата возврата (по определению процедуры[Примечание 1]).
  • процедуры создания поименованы (имена ограничены допустимыми идентификаторами);
  • процедуры создания задаются по именам в тексте класса;
  • процедуры создания могут быть вызваны напрямую (как обычные процедуры) для повторной инициализации объектов;
  • каждый эффективный (то есть конкретный, не абстрактный) класс должен (явно или неявно) указать по крайней мере одну процедуру создания;
  • процедуры создания отвечают за приведение только что проинициализированного объекта в состояние, которое удовлетворяет инварианту класса[Примечание 2].

Хотя создание объекта является предметом некоторых тонкостей [Примечание 3], создание атрибута с типовым объявлением x: T, выраженном в виде инструкции создания create x.make состоит из следующей последовательности шагов:

  • создать новый непосредственный экземпляр типа T[Примечание 4];
  • выполнить процедуру создания make для вновь созданного экземпляра;
  • прикрепить вновь созданный объект к сущности x.

Пример

В первом отрывке ниже определяется класс POINT. Процедура make кодируется после ключевого слова feature.

Ключевое слово create вводит список процедур, которые могут быть использованы для инициализации экземпляров класса. В данном случае список содержит default_create, процедуру с пустой реализацией, унаследованной из класса ANY, и процедуру make с реализацией в самом классе POINT.

class
    POINT
create
    default_create, make
 
feature
 
    make (a_x_value: REAL; a_y_value: REAL)
        do
            x := a_x_value
            y := a_y_value
        end
 
    x: REAL
            -- Координата X
 
    y: REAL
            -- Координата Y
        ...

Во втором отрывке класс, являющийся клиентом класса POINT, имеет объявления my_point_1 и my_point_2 типа POINT.

В коде подпрограммы my_point_1 создаётся с координатами (0.0; 0.0). Поскольку в инструкции создания не указана процедура создания, используется процедура default_create, унаследованная из класса ANY. Эта же строка могла бы быть переписана как create my_point_1.default_create. Только процедуры, указанные как процедуры создания могут использоваться в инструкциях создания (то есть в инструкциях с ключевым словом create).

Следующей идёт инструкция создания для my_point_2, задающая начальные значения для координат my_point_2.

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

    my_point_1: POINT
    my_point_2: POINT
        ...
 
            create my_point_1
            create my_point_2.make (3.0, 4.0)
            my_point_2.make (5.0, 8.0)
        ...

ColdFusion

Пример

Необходимо отметить, что в ColdFusion не существует метода-конструктора. Широкое распространение среди сообщества программистов на ColdFusion получил способ вызова метода ‘init‘, выступающего в качестве псевдоконструктора.

<cfcomponent displayname="Cheese">
   <!--- свойства --->
   <cfset variables.cheeseName = "" />
   <!--- псевдоконструктор --->
   <cffunction name="init" returntype="Cheese">
      <cfargument name="cheeseName" type="string" required="true" />
      <cfset variables.cheeseName = arguments.cheeseName />
      <cfreturn this />
   </cffunction>
</cfcomponent>

PHP

Пример

В PHP (начиная с версии 5) конструктор — это метод __construct(), который автоматически вызывается ключевым словом new после создания объекта. Обычно используется для выполнения различных автоматических инициализаций, как например, инициализация свойств. Конструкторы также могут принимать аргументы, в этом случае, когда указано выражение new, необходимо передать конструктору формальные параметры в круглых скобках.

class Person
{
   private $name;
 
   function __construct($name)
   {
       $this->name = $name;
   }
 
   function getName()
   {
       return $this->name;
   }
}

Тем не менее, конструктор в PHP версии 4 (и ранее) — метод класса с именем этого же класса.

class Person
{
   private $name;
 
   function Person($name)
   {
       $this->name = $name;
   }
 
   function getName()
   {
       return $this->name;
   }
}

Perl

Пример

В Perl конструктор должен применить функцию bless к некой переменной (обычно ссылке на хеш):

package Example;
 
sub new {
  my $class = shift;
  my $self  = {};
  return bless $self, $class;
}
 
1;

Но это минимальный базовый вариант, есть множество более продвинутых способов, начиная от use fields и заканчивая Moose.

Упрощенные конструкторы (с псевдокодом)

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

class Student {
    // описание класса учеников
    // ... прочий код ...
}

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

class Student {
    Student (String studentName, String Address, int ID) {
        // ... здесь храним вводимые данные и прочие внутрнние поля ...
    }
    // ...
}

Примечания

  1. Подпрограммы Эйфеля являются либо процедурами либо функциями. У процедур нет никакого возвращаемого типа. Функции всегда имеют возвращаемый тип.
  2. Поскольку должен быть также удовлетворён инвариант наследуемого(-ых) класса(-ов), нет обязательного требования вызова родительских конструкторов.
  3. Полная спецификация содержится в стандартах ISO/ECMA по языку программироная Эйфель в он-лайн доступе.[2]
  4. Стандарт Эйфеля требует, чтобы поля были инициализированы при первом доступе к ним, т.ч. нет необходимости осуществлять их инициализацию значениями по умолчанию во время создания объекта.

Ссылки

  1. Конечно, это приводит к определённым техническим трудностям — например, что будет, если из конструктора выпадет исключение? Впрочем, разработчик класса просто должен выполнять требования языка, а в большинстве программ не требуется детальная диагностика и автоматические повторы при ошибках.
  2. ISO/ECMA документ описания Эйфеля
  • Конструкторы и деструкторы

См. также

  • Деструктор

У этого термина существуют и другие значения, см. Конструктор.

В объектно-ориентированном программировании конструктор класса (от англ. constructor, иногда сокращают ctor) — специальный блок инструкций, вызываемый при создании объекта.

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

Термин «конструктор» также используется для обозначения одного из тегов, описывающих данные в алгебраическом типе данных. Это использование несколько отличается от описываемого в статье. Для дополнительной информации смотрите Алгебраический тип данных.

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

Содержание

  • 1 Назначение конструктора
  • 2 Виды конструкторов
    • 2.1 Конструктор по умолчанию
    • 2.2 Конструктор копирования
    • 2.3 Конструктор преобразования
    • 2.4 Виртуальный конструктор
  • 3 Синтаксис
    • 3.1 С++
      • 3.1.1 Пример
    • 3.2 Python
      • 3.2.1 Пример
    • 3.3 Delphi
      • 3.3.1 Пример
    • 3.4 Java
      • 3.4.1 Пример
    • 3.5 JavaScript
      • 3.5.1 Пример
    • 3.6 Visual Basic .NET
      • 3.6.1 Пример
    • 3.7 C#
      • 3.7.1 Пример
    • 3.8 Эйфель
      • 3.8.1 Пример
    • 3.9 ColdFusion
      • 3.9.1 Пример
    • 3.10 PHP
      • 3.10.1 Пример
  • 4 Упрощенные конструкторы (с псевдокодом)
  • 5 Примечания
  • 6 Ссылки
  • 7 См. также

[править] Назначение конструктора

Одна из ключевых особенностей ООП — инкапсуляция: внутренние поля объекта напрямую недоступны, и пользователь может работать с объектом только как с единым целым, через открытые (public) методы. Каждый метод, в идеале, должен быть устроен так, чтобы объект, находящийся в «допустимом» состоянии (то есть когда выполняется инвариант класса), после вызова метода также оказался в допустимом состоянии. И первая задача конструктора — перевести поля объекта в такое состояние.

Вторая задача — упростить пользование объектом. Объект — не «вещь в себе», ему часто приходится требовать какую-то информацию от других объектов: например, объект File, создаваясь, должен получить имя файла. Это можно сделать и через метод:

  File file;
  file.open("in.txt", File::omRead);

Но удобнее открытие файла сделать в конструкторе:[1]

  File file("in.txt", File::omRead);

[править] Виды конструкторов

Некоторые языки программирования различают несколько особых типов конструкторов:

  • конструктор по умолчанию — конструктор, не принимающий аргументов;
  • конструктор копирования — конструктор, принимающий в качестве аргумента объект того же класса (или ссылку из него);
  • конструктор преобразования — конструктор, принимающий один аргумент (эти конструкторы могут вызываться автоматически для преобразования значений других типов в объекты данного класса).
class Complex
{
 public:
  // Конструктор по умолчанию 
  // (в данном случае является также и конструктором преобразования)
  Complex(double i_re = 0, double i_im = 0)
      : re(i_re), im(i_im)
  {}
 
  // Конструктор копирования
  Complex(const Complex &obj)
  {
   re = obj.re;
   im = obj.im;
  }
  private:
    double re, im;
};

[править] Конструктор по умолчанию

Конструктор не имеющий обязательных аргументов. Используется при создании массивов объектов, вызываясь для создания каждого экземпляра. В отсутствие явно заданного конструктора по умолчанию его код генерируется компилятором (что на исходном тексте, естественно, не отражается).

[править] Конструктор копирования

Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функции по значению.

Конструктор копирования в основном необходим, когда объект имеет указатели на объекты выделенные в куче. Если программист не создаёт конструктор копирования, то компилятор создаст неявный конструктор копирования, который копирует указатели как есть, то есть фактическое копирование данных не происходит и два объекта ссылаются на одни и те же данные в куче. Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе.

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

[править] Конструктор преобразования

Конструктор, принимающий один аргумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально.

[править] Виртуальный конструктор

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

«Виртуальными конструкторами» называют похожий, но другой механизм, присутствующий в некоторых языках — например, он есть в Delphi, но нет в C++ и Java. Этот механизм позволяет создать объект любого заранее неизвестного класса при двух условиях:

  • этот класс является потомком некоего наперёд заданного класса (в данном примере это класс TVehicle);
  • на всём пути наследования от базового класса к создаваемому цепочка переопределения не обрывалась. При переопределении виртуального метода синтаксис Delphi требует ключевое слово overload, чтобы старая и новая функции с разными сигнатурами могли сосуществовать, override для переопределения функции либо reintroduce для задания новой функции с тем же именем — последнее недопустимо.
type
  TVehicle = class
      constructor Create;  virtual;
    end;
 
  TAutomobile = class (TVehicle)
      constructor Create;  override;
    end;
 
  TMotorcycle = class (TVehicle)
      constructor Create;  override;
    end;
 
  TMoped = class (TMotorcycle)  // обрываем цепочку переопределения - заводим новый Create
      constructor Create(x : integer);  reintroduce;
    end;

В языке вводится так называемый классовый тип (метакласс). Этот тип в качестве значения может принимать название любого класса, производного от TVehicle.

type
  CVehicle = class of TVehicle;

Такой механизм позволяет создавать объекты любого заранее неизвестного класса, производного от TVehicle.

var
  cv : CVehicle;
  v : TVehicle;
 
cv := TAutomobile;
v := cv.Create;

Заметьте, что код

cv := TMoped;
v := cv.Create;

является некорректным — директива reintroduce разорвала цепочку переопределения виртуального метода, и в действительности будет вызван конструктор TMotorcycle.Create (а значит, будет создан мотоцикл, а не мопед!)

См. также Фабрика (шаблон проектирования)

[править] Синтаксис

[править] С++

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

[править] Пример

class ClassWithConstructor {
 public:
  /* Инициализация внутреннего объекта с помощью конструктора */
  ClassWithConstructor(float parameter): object(parameter) {}/* вызов конструктора AnotherClass(float); */
 private:
  AnotherClass object;
};

[править] Python

В языке Python конструктором является метод класса с именем __init__ . Кроме того не следует забывать, что первым аргументом любого метода должен быть указатель на контекст класса self.

[править] Пример

class ClassWithConstructor:
 
    def __init__(self):
        """This method is constructor."""
        pass

[править] Delphi

В Delphi, в отличие от C++, для объявления конструктора служит ключевое слово constructor. Имя конструктора может быть любым, но рекомендуется называть конструктор Create.

[править] Пример

  TClassWithConstructor = class
    public
    constructor Create;
  end;

[править] Java

Некоторые отличия между конструкторами и другими методами Java:

  • конструкторы не имеют чётко определённого типа возвращаемых данных;
  • конструкторы не могут напрямую вызываться (необходимо использовать ключевое слово new);
  • конструкторы не могут быть synchronized, final, abstract, native и static типов;
  • конструкторы всегда выполняются в том же потоке.

[править] Пример

public class Example{
  // Конструктор по умолчанию
  public Example(){
    this(1);
  }
 
  // Перегрузка конструктора
  public Example(int input){
    data = input;
  }
 
  private int data;
}
// код, иллюстрирующий создание объекта описанным выше конструктором
Example e = new Example(42);

[править] JavaScript

В JavaScript в качестве конструктора выступает обычная функция, используемая в качестве операнда оператора new. Для обращения к созданному объекту используется ключевое слово this.

[править] Пример

function Example(initValue) {
    this.myValue = initValue;
}
 
Example.prototype.getMyValue = function() {
    return this.myValue; 
}
// код, иллюстрирующий создание объекта описанным выше конструктором
var exampleObject = new Example(120);

[править] Visual Basic .NET

Конструкторы в Visual Basic .NET используют обычный метод объявления с именем New.

[править] Пример

Class Foobar
  Private strData As String
 
  ' Constructor
  Public Sub New(ByVal someParam As String)
     strData = someParam
  End Sub
End Class
' некий код
' иллюстрирующий создание объекта описанным выше конструктором
Dim foo As New Foobar(".NET")

[править] C#

[править] Пример

class myClass
{
  private int mA;
  private string mB;
 
  public myClass(int a, string b)
  {
    mA = a;
    mB = b;
  }
}
// код, иллюстрирующий создание объекта описанным выше конструктором
myClass c = new myClass(42, "string");

[править] Эйфель

В Эйфеле подпрограммы, которые инициализируют объекты, называются процедурами создания. Процедуры создания в чём-то подобны конструкторам и в чём-то отличаются. Они имеют следующие характеристики:

  • Процедуры создания не имеют никакого явного типа результата возврата (по определению процедуры[Примечание 1]).
  • процедуры создания поименованы (имена ограничены допустимыми идентификаторами);
  • процедуры создания задаются по именам в тексте класса;
  • процедуры создания могут быть вызваны напрямую (как обычные процедуры) для повторной инициализации объектов;
  • каждый эффективный (то есть конкретный, не абстрактный) класс должен (явно или неявно) указать по крайней мере одну процедуру создания;
  • процедуры создания отвечают за приведение только что проинициализированного объекта в состояние, которое удовлетворяет инварианту класса[Примечание 2].

Хотя создание объекта является предметом некоторых тонкостей [Примечание 3], создание атрибута с типовым объявлением x: T, выраженном в виде инструкции создания create x.make состоит из следующей последовательности шагов:

  • создать новый непосредственный экземпляр типа T[Примечание 4];
  • выполнить процедуру создания make для вновь созданного экземпляра;
  • прикрепить вновь созданный объект к сущности x.

[править] Пример

В первом отрывке ниже определяется класс POINT. Процедура make кодируется после ключевого слова feature.

Ключевое слово create вводит список процедур, которые могут быть использованы для инициализации экземпляров класса. В данном случае список содержит default_create, процедуру с пустой реализацией, унаследованной из класса ANY, и процедуру make с реализацией в самом классе POINT.

class
    POINT
create
    default_create, make
 
feature
 
    make (a_x_value: REAL; a_y_value: REAL)
        do
            x := a_x_value
            y := a_y_value
        end
 
    x: REAL
            -- Координата X
 
    y: REAL
            -- Координата Y
        ...

Во втором отрывке класс, являющийся клиентом класса POINT, имеет объявления my_point_1 и my_point_2 типа POINT.

В коде подпрограммы my_point_1 создаётся с координатами (0.0; 0.0). Поскольку в инструкции создания не указана процедура создания, используется процедура default_create, унаследованная из класса ANY. Эта же строка могла бы быть переписана как create my_point_1.default_create. Только процедуры, указанные как процедуры создания могут использоваться в инструкциях создания (то есть в инструкциях с ключевым словом create).

Следующей идёт инструкция создания для my_point_2, задающая начальные значения для координат my_point_2.

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

    my_point_1: POINT
    my_point_2: POINT
        ...
 
            create my_point_1
            create my_point_2.make (3.0, 4.0)
            my_point_2.make (5.0, 8.0)
        ...

[править] ColdFusion

[править] Пример

Необходимо отметить, что в ColdFusion не существует метода-конструктора. Широкое распространение среди сообщества программистов на ColdFusion получил способ вызова метода ‘init‘, выступающего в качестве псевдоконструктора.

<cfcomponent displayname="Cheese">
   <!--- свойства --->
   <cfset variables.cheeseName = "" />
   <!--- псевдоконструктор --->
   <cffunction name="init" returntype="Cheese">
      <cfargument name="cheeseName" type="string" required="true" />
      <cfset variables.cheeseName = arguments.cheeseName />
      <cfreturn this />
   </cffunction>
</cfcomponent>

[править] PHP

[править] Пример

В PHP (начиная с версии 5) конструктор — это метод __construct(), который автоматически вызывается ключевым словом new после создания объекта. Обычно используется для выполнения различных автоматических инициализаций, как например, инициализация свойств. Конструкторы также могут принимать аргументы, в этом случае, когда указано выражение new, необходимо передать конструктору формальные параметры в круглых скобках.

class Person
{
   private $name;
 
   function __construct($name)
   {
       $this->name = $name;
   }
 
   function getName()
   {
       return $this->name;
   }
}

Тем не менее, конструктор в PHP версии 4 (и ранее) — метод класса с именем этого же класса.

class Person
{
   private $name;
 
   function Person($name)
   {
       $this->name = $name;
   }
 
   function getName()
   {
       return $this->name;
   }
}

[править] Упрощенные конструкторы (с псевдокодом)

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

class Student {
    // описание класса учеников
    // ... прочий код ...
}

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

class Student {
    Student (String studentName, String Address, int ID) {
        // ... здесь храним вводимые данные и прочие внутрнние поля ...
    }
    // ...
}

[править] Примечания

  1. Подпрограммы Эйфеля являются либо процедурами либо функциями. У процедур нет никакого возвращаемого типа. Функции всегда имеют возвращаемый тип.
  2. Поскольку должен быть также удовлетворён инвариант наследуемого(-ых) класса(-ов), нет обязательного требования вызова родительских конструкторов.
  3. Полная спецификация содержится в стандартах ISO/ECMA по языку программироная Эйфель в он-лайн доступе.[2]
  4. Стандарт Эйфеля требует, чтобы поля были инициализированы при первом доступе к ним, т.ч. нет необходимости осуществлять их инициализацию значениями по умолчанию во время создания объекта.

[править] Ссылки

  1. Конечно, это приводит к определённым техническим трудностям — например, что будет, если из конструктора выпадет исключение? Впрочем, разработчик класса просто должен выполнять требования языка, а в большинстве программ не требуется детальная диагностика и автоматические повторы при ошибках.
  2. ISO/ECMA документ описания Эйфеля
  • Конструкторы и деструкторы

[править] См. также

  • Деструктор

В объектно-ориентированном
программировании
 конструктор класса
(от англ. constructor,
иногда сокращают ctor) —
специальный блок инструкций, вызываемый
при создании объекта.

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

Термин
«конструктор» также используется для
обозначения одного из тегов, описывающих
данные в алгебраическом
типе данных
.
Это использование несколько отличается
от описываемого в статье. Для дополнительной
информации смотрите Алгебраический
тип данных
.

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

Назначение конструктора

Одна
из ключевых особенностей ООП — инкапсуляция:
внутренние поля объекта напрямую
недоступны, и пользователь может работать
с объектом только как с единым целым,
через открытые (public)
методы. Каждый метод, в идеале, должен
быть устроен так, чтобы объект, находящийся
в «допустимом» состоянии (то есть когда
выполняется инвариант класса),
после вызова метода также оказался в
допустимом состоянии. И первая задача
конструктора — перевести поля объекта
в такое состояние.

Вторая
задача — упростить пользование
объектом. Объект — не «вещь
в себе»,
ему часто приходится требовать какую-то
информацию от других объектов: например,
объектFile,
создаваясь, должен получить имя файла.
Это можно сделать и через метод:

Виды конструкторов

Некоторые
языки программирования различают
несколько особых типов конструкторов:

  • конструктор
    по умолчанию
     —
    конструктор, не принимающий аргументов;

  • конструктор
    копирования
     —
    конструктор, принимающий в качестве
    аргумента объект того же класса (или
    ссылку из него);

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

Конструктор по умолчанию

Основная
статья
Конструктор
по умолчанию

Конструктор
не имеющий обязательных аргументов.
Используется при создании массивов
объектов, вызываясь для создания каждого
экземпляра. В отсутствие явно заданного
конструктора по умолчанию его код
генерируется компилятором (что на
исходном тексте, естественно, не
отражается).

Конструктор копирования

Основная
статья
Конструктор
копирования

Конструктор,
аргументом которого является ссылка на
объект того же класса. Применяется
в C++ для
передачи объектов в функции по
значению
.

Конструктор
копирования в основном необходим, когда
объект имеет указатели на объекты
выделенные в куче.
Если программист не создаёт конструктор
копирования, то компилятор
создаст неявный конструктор
копирования, который копирует указатели как
есть
,
то есть фактическое копирование данных
не происходит и два объекта ссылаются
на одни и те же данные в куче. Соответственно
попытка изменения «копии» повредит
оригинал, а вызов деструктора для одного
из этих объектов при последующем
использовании другого приведёт к
обращению в область памяти, уже не
принадлежащую программе.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #

    15.02.20166.33 Mб613kurs_ekonomicheskoy_teorii_uchebnik (2).doc

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

1. Классы в языке Си++: объявление, поля и функции (методы) классов. Определение функций внутри класса и за пределами класса, примеры.

Классы

Класс в языке Си++ – это новый тип, определяемый программистом, 
включающий данные (поля класса) и методы (функции) для обработки этих данных. 
Переменные этого типа называются объектами.
Можно дать такое определение класса через структуру, которая была в языке Си.

Класс – это структура, в которую введены методы для обработки полей.
Объекты – это переменные типа класса. 

Объявление

Формат объявления класса:
<Ключевое слово> <Имя_класса>
{
 <список компонент>
};

В качестве ключевого слова используется одно из трех ключевых слов:
- struct
- class
- union
Формат определения объектов:
<Имя_класса> <Имя_объекта1>,…<Имя_объекта_N>;

Поля и методы

struct Complex {
    double real,  image; // Поля класса 
    void define(double re=0.0, double im=0.0)   // 
	// Определение метода внутри класса, метод будет подставляемым
    {
    real=re; image=im;// Обращение к полям внутри метода
    }
    void print(); // Описание метода
};

void Complex::print() // Определение метода за пределами класса
{
    printf("nreal=%f  image=%f", real, image); 
}

Перегрузки

Возможна перегрузка методов.
Чаще всего класс объявляется в 2-х файлах: 
− заголовочный файл с расширением .h, содержит описание класса с заголовками методов; 
− файл реализации с расширением .cpp, содержит определения методов за пределами класса.

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

Инициализация выполняется до выполнения конструктора. 
Функция класса может иметь модификатор const, 
это означает, что она не может изменять поля класса, 
кроме полей с модификатором mutable
class point
{
    int x=-10;
    mutable int y=-10;
public:
    void print() const;
……
};

void point::print() const // Определение метода за пределами класса с модификатором const
{
    cout<<"nx="<<x<<" y="<<y; 
	y = 10; // Было бы запрещено, если бы y был без mutable
}

Конструктор

Конструктор класса специальный блок операторов (инструкций), 
вызываемый при создании объекта. 
Назначение: 
присвоение нач. значений полям, 
выделение памяти, 
открытие файлов, 
сетевых соединений и т.п. 

Имя конструктора совпадает с именем класса, 
конструктор не имеет возвращаемого значения. 

Возможна перегрузка конструкторов. 
Конструктор может определяться как внутри класса, так и за пределами.
Формат определения конструктора внутри класса: 
Имя_класса(Список_формальных_параметров) {Операторы_тела конструктора} 

По умолчанию класс всегда имеет конструктор копирования вида 
A(const A& a) {… } (A – имя класса), 
создающий копию объекта (происходит копирование полей), 
и, если нет ни одного явного конструктора, 
то по умолчанию создается конструктор без параметров. 
Эти конструкторы можно переопределять. 

Конструктор копирования по умолчанию можно удалить, 
для этого в классе заголовок конструктора объявляется с ключевым словом delete без тела, 
например, A(const A& a)=delete; 
Если нет явного конструктора копирования, 
то конструктор копирования по умолчанию будет удален при наличии явного конструктора перемещения 
(см. ниже) или оператора копирования с перемещением.
Примеры вызовов конструкторов:
    A a1;  A* pA=new A; // Вызываются конструкторы без параметров
    A a2(3, 4); A * pA2=new A(3, 4);
    // Вызываются конструкторы с 2-мя параметрами 

Для конструктора с одним параметром можно использовать форму:
    A a1=5; A a2=a1; 
	// Вместо 
	A a1(5); A a2(a1);
	
Ключевое слово explicit у конструктора с одним параметром запрещает это преобразование, 
допустима явная форма: 
    A a1(5); A a2(a1);
Пример:
class point
{
    int x, y;
public:
    point(int, int); // Конструктор с 2-мя параметрами 
	explicit point(int); // Конструктор с 1-м параметром 
	point() : x(0), y(0)  // Можно иницилизировать поля так 
	{ // Конструктор без параметров 
		// x=0; y=0;     // Или так
        cout << endl << "point()";
    }
    void print(); 
	~point(){
    printf("nDestructor");
    }
};

point::point(int a, int b) // Определение конструктора за пределами класса 
    : x(a), y(b) // Можно так
{
    // x=a; y=b; // Или так
}

point::point(int a) // Определение конструктора за пределами класса
{
    x=a; y=0;
}

void point::print() // Определение метода за пределами класса
{
	cout<<"nx="<<x<<" y="<<y;
}

	int main(int argc, char* argv[]) {
	point *pP=new point[10]; // Массив указателей на объеты, создается 10 объектов, 
			 // вызываются конструкторы без параметров
	point p1(12, 13); 
	p1.print();
	point p2; // Вызов конструктора без параметров 
	point p3=100; // point p3(100); Вызов конструктора с 1-м параметром Ошибка, надо
				// point p3(100); так как есть explicit у конструктора
	p2.print(); 
	p3.print();
	point p4=p1; // point p4(p1);  Вызов конструктора копирования
	p4.print(); 
	delete [] pP;
	// free(pP); Так не верно
	return 0;
}
Обращение к полям и методам класса внутри методов класса просто по имени, 
а за пределами класса через имя объекта и операцию «.» 
или через имя указателя на объект и операцию «->». 
Каждый объект класса имеет в оперативной памяти свои копии полей класса.
Пример работы с полями:
struct A { 
	int i; 
	void print() { 
		printf(“i=%d”, i);
	} 
};

A a1;
A *pA=&a1;

a1.i=10; 
a1.print();

pA->i=10; 
pA->print();

a1.A::i=10; 
a1.A::print();

pA->A::i=10; 
pA->A::print();
Компонентные данные и функции класса
Компонентные данные (поля класса) и функции класса (методы класса) уже во
многом рассмотрены выше. 
Следует добавить, что методы класса могут быть определены внутри класса, 
в этом случае они по возможности (если нет ограничений) являются подставляемыми, 
но чаще всего класс содержит описание (заголовки) методов, 
а определения методов находятся за пределами класса. 

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

Создается отдельный файл с расширением .h (заголовочный файл), 
в котором находится описание класса вместе с полями и заголовками методов. 
Определения методов находится в файле реализации класса 
(файле с расширением .cpp). 

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

В классах возможна перегрузка методов.
В следующем примере показаны перечисленные возможности:
struct Complex {
    double real,  
    image; // Поля класса 
    void define(double re=0.0, double im=0.0) 
    // Определение метода внутри класса, метод  будет  подставляемым
    {
        real=re; image=im;// Обращение к полям внутри метода
    }
    void print(); // Описание метода
};
void Complex::print() // Определение метода за пределами класса
{
    printf("nreal=%f  image=%f", real, image); 
}

2. Статусы доступа компонент класса. Операции для доступа к компонентам класса, примеры.

Доступность компонент класса
Свойство доступности определяет возможность доступа к полям и методам за
пределами класса (через имя объекта или через указатель на объект).

Существуют три статуса доступа:
• public (полностью доступны за пределами класса);
• private (не доступны за пределами класса, можно обращаться к компонентам
только в методах своего класса);
• protected (доступны только в своем классе и в производных классах).
По умолчанию, если класс определен с ключевым словом struct, 
то все компоненты имеют статус доступа public. 

Если с ключевым словом union, тоже public, 
но все поля каждого объекта располагаются в памяти, 
начиная с одного адреса. 

Если класс определен с ключевым словом class, 
то все поля и методы по умолчанию имеют статус доступа private.
Статус доступа можно изменить с помощью соответствующих модификаторов, 
что продемонстрировано в следующем примере:

struct A
{
 …… // Статус доступа public
private:
………. // Статус доступа private
protected:
……….// Статус доступа protected
};

class B
{
 …… // Статус доступа private
public:
………. // Статус доступа public
protected:
……….// Статус доступа protected
}; 
Обращение к полям и методам класса внутри методов класса просто по имени, 
а за пределами класса через имя объекта и операцию «.» 
или через имя указателя на объект и операцию «->». 
Каждый объект класса имеет в оперативной памяти свои копии полей класса.
Пример работы с полями:
struct A { 
	int i; 
	void print() { 
		printf(“i=%d”, i);
	} 
};

A a1;
A *pA=&a1;

a1.i=10; 
a1.print();

pA->i=10; 
pA->print();

a1.A::i=10; 
a1.A::print();

pA->A::i=10; 
pA->A::print();

get set

#include <iostream> 
#include <vector> 
#include <string>

using namespace std;

class Avt // Класс автомобиль
{
    string marka; // Марка
    double rash; // Расход топлива на 100 км
public:
    Avt()  // Конструктор без параметров создает "пустой" объект
    {
        rash = 0; 
	marka = "";
    }
    
	void set(string mar, double r) // Функция для инициализации полей  
	                               // Для созданного "пустого" объекта
    {
        marka= mar; // Копируем строку, содержащую марку автомобиля 
	rash = r; // Задаем значение расхода топлива на 100 км
    }
    
	Avt(string mar, double r) // Конструктор для инициализации полей
    {
        marka=mar; // Копируем строку, содержащую марку автомобиля 
	rash = r; // Задаем значение расхода топлива на 100 км
    }
    
	double getRash(double dlina) const // Функция возвращает - сколько нужно топлива для
    // пробега заданного расстояния
    {
        return rash * dlina / 100.;
    }
    
	void print() const // Функция для печати полей объекта
    {
        cout << "nmarka: " << marka << "  rashod na 100 km=" << rash;
    }
};

int main() {
	int n; // Неизвестное число объектов 
	cout << "n="; cin >> n;  // Ввод с клавиатуры n 
	double rast; // Расстояние, для которого требуется вычислить расход топлива 
	cout << "rast="; cin >> rast; // Ввод с клавиатуры расстояния 
	double SumRashod = 0; // Суммарный расход топлива для всех автомобилей 
	string str; double r; // Вспомогательные переменные для ввода марки 
						  // автомобиля и расхода топлива 

Первый способ создаем массив «пустых» объектов и инициализируем их с помощью функции set

	Avt *pAvt; // Указатель на массив 
	pAvt=new Avt[n]; // Для каждого объекта вызывается конструктор без параметров, 
				 	// т.е. созданы "пустые" объекты 
	vector<Avt> vecAvt(n); // Создаем пустые объекты, вызывается конструктор без параметров 
    
	// Цикл ввода данных для объектов
	for(int i=0; i<n; i++) {
    
	cout<<"Object N="<<(i+1)<<":n"<<"marka: ";
	cin.ignore();
    
	getline(cin, str); // Ввод марки автомобиля 
	cout<<"Rashod="; cin>>r;  // Ввод расхода топлива 
	pAvt[i].set(str, r); // Вызываем функцию set для инициализации полей 
	vecAvt[i].set(str, r); // Инициализация для vector объектов
	}
    
	// Цикл печати полей для объектов
	for(int i=0; i<n; i++) pAvt[i].print();
    
	// Цикл для расчета суммарного расхода топлива
	for(int i=0; i<n; i++) SumRashod+=pAvt[i].getRash(rast);
	cout << "nSumRashor=" << SumRashod;  // Вывод на печать суммарного расхода топлива 
    
	// Тоже делаем для vector
	SumRashod = 0;
    
	// Цикл печати полей для объектов
	for (const auto pos : vecAvt) pos.print();
    
	// Цикл для расчета суммарного расхода топлива
	for (const auto pos : vecAvt) SumRashod += pos.getRash(rast);
	cout << "nSumRashor=" << SumRashod;  // Вывод на печать суммарного расхода топлива 
	
	return 0;
}

Второй способ создаем массив указателей на объекты и далее каждый объект создается динамически с помощью конструктора с инициализацией

	Avt **ppA; // Указатель на массив указателей 
	ppA = new Avt*[n]; // Создаем массив указателей 
	vector<Avt> vecAvt; // Параллельно создаем вектор автомобилей 
	
	for (int i = 0; i < n; i++) {
		cout << "Object N=" << (i + 1) << ":n" << "marka: "; 
		cin.ignore(); 
		getline(cin, str); // Ввод марки автомобиля 
		cout << "Rashod="; cin >> r; // Ввод расхода топлива 
		ppA[i] = new Avt(str, r); // Создание объекта динамически 
                                  // с вызовом конструктора с параметрами 
		vecAvt.push_back(Avt(str, r)); // Добавляем объект в контейнер 
	}
	// Цикл печати полей для объектов 
	for (int i = 0; i < n; i++) ppA[i]->print(); 
	// Цикл для расчета суммарного расхода топлива
    
	for (int i = 0; i < n; i++) SumRashod += ppA[i]->getRash(rast); 
    
	cout << "nSumRashor=" << SumRashod;  // Вывод на печать суммарного расхода топлива 
    
	// Тоже делаем для vector 
	SumRashod = 0; // Цикл печати полей для объектов 
    
	for (const auto pos : vecAvt) pos.print(); 
	// Цикл для расчета суммарного расхода топлива 
    
	for (const auto pos : vecAvt) SumRashod += pos.getRash(rast); 
	cout << "nSumRashor=" << SumRashod;  // Вывод на печать суммарного расхода топлива 
    
	return 0;
}

3. Конструкторы и деструктор класса, конструктор копирования и конструктор перемещения, назначение, пример.

Конструкторы

Конструктор класса – специальный блок операторов (инструкций), 
вызываемый при создании объекта. 

Назначение: 
    присвоение начальных значений полям, 
    выделение памяти, 
    открытие файлов, 
    сетевых соединений и т.п. 

Имя конструктора совпадает с именем класса, 
конструктор не имеет возвращаемого значения. 

Возможна перегрузка конструкторов.
Конструктор может определяться как внутри класса, так и за пределами.

Формат определения конструктора внутри класса: 
    Имя_класса(Список_формальных_параметров) {Операторы_тела конструктора} 

По умолчанию класс всегда имеет конструктор копирования вида 
    A(const A& a) {… } (A – имя класса), 
создающий копию объекта (происходит копирование полей), 
и, если нет ни одного явного конструктора, 
то по умолчанию создается конструктор без параметров. 
Эти конструкторы можно переопределять. 

Конструктор копирования по умолчанию можно удалить, 
для этого в классе заголовок конструктора 
объявляется с ключевым словом delete без тела, 
например, 
    A(const A& a)=delete; 

Если нет явного конструктора копирования, 
то конструктор копирования по умолчанию будет удален при наличии явного конструктора перемещения 
(см. ниже) или оператора копирования с перемещением.
Примеры вызовов конструкторов:
    A a1;  A* pA=new A; // Вызываются конструкторы без параметров
    A a2(3, 4); A * pA2=new A(3, 4);
    // Вызываются конструкторы с 2-мя параметрами 

Для конструктора с одним параметром можно использовать форму:
    A a1=5; A a2=a1; 
	// Вместо 
	A a1(5); A a2(a1);
	
Ключевое слово explicit у конструктора с одним параметром запрещает это преобразование, 
допустима явная форма: 
    A a1(5); A a2(a1);
Пример:
class point
{
    int x, y;
public:
    point(int, int); // Конструктор с 2-мя параметрами 
	explicit point(int); // Конструктор с 1-м параметром 
	point() : x(0), y(0)  // Можно иницилизировать поля так 
	{ // Конструктор без параметров 
		// x=0; y=0;     // Или так
        cout << endl << "point()";
    }
    void print(); 
	~point(){
    printf("nDestructor");
    }
};

point::point(int a, int b) // Определение конструктора за пределами класса 
    : x(a), y(b) // Можно так
{
    // x=a; y=b; // Или так
}

point::point(int a) // Определение конструктора за пределами класса
{
    x=a; y=0;
}

void point::print() // Определение метода за пределами класса
{
	cout<<"nx="<<x<<" y="<<y;
}

	int main(int argc, char* argv[]) {
	point *pP=new point[10]; // Массив указателей на объеты, создается 10 объектов, 
			 // вызываются конструкторы без параметров
	point p1(12, 13); 
	p1.print();
	point p2; // Вызов конструктора без параметров 
	point p3=100; // point p3(100); Вызов конструктора с 1-м параметром Ошибка, надо
				// point p3(100); так как есть explicit у конструктора
	p2.print(); 
	p3.print();
	point p4=p1; // point p4(p1);  Вызов конструктора копирования
	p4.print(); 
	delete [] pP;
	// free(pP); Так не верно
	return 0;
}

Конструктор перемещения

Конструктор перемещения (move constructor)
В отличие от конструктора копирования 
(этот конструктор «работает», как правило, быстрее), 
исходный объект уже не нужен, 
но его нужно привести в форму, 
подходящую для вызова деструктора, 
чтобы  «не испортить» новый объект.

Пример: Vec(Vec&& v)  // Параметр - правосторонняя ссылка
//   : p(v.p), len(v.len) // Можно инициализировать так 
{
    p = v.p; 
	len = v.len;
    // Присвойте данным-членам исходного объекта значения по умолчанию. 
	// Это не позволяет деструктору многократно освобождать память
    v.p = nullptr; 
	v.len = 0; 
}
Вызов конструктора: Vec V2=std::move(V1); 

#include <stdlib.h>
#include <iostream>

class Massiv
{
public:
    int *p=nullptr; // Изначально "пустой" объект
    int n=0; 
	
	Massiv(int *pp, int nn);
    Massiv(const Massiv &M);
    
	// Конструктор перемещения 
	Massiv(Massiv&& M);  // Параметр - правосторонняя ссылка 
	
	~Massiv(); // Описание деструктора
    
	void print() const;
};
Massiv::~Massiv()
{
    if (p != nullptr) delete[]p;
    std::cout << "Destryctorn";
}

Massiv::Massiv(const Massiv & M)
{ // Новый конструктор копирования std::cout <<"Constr Copyn";
    n = M.n;
    p = new int[n];
    for (int i = 0; i<n; i++)
    p[i] = M.p[i];
}
// Конструктор перемещения (вариант: делаем все явно) 
/*
Massiv::Massiv(Massiv&& M)  // Параметр - правосторонняя ссылка
        : p(M.p), n(M.n)   // Можно инициализировать так 
{	
    std::cout << "Constr Moven"; 
    //p = M.p;  // Или инициализировать так 
    //n = M.n; 
    
    // Присвойте данным-членам исходного объекта значения по умолчанию. 
    // Это не позволяет деструктору многократно освобождать память
    
    M.p = nullptr; 
    M.n = 0; 
}
*/
// Конструктор перемещения (более короткий вариант) 
Massiv::Massiv(Massiv&& M)  // Параметр - правосторонняя ссылка
{
    std::cout << "Constr Moven";
    std::swap(p, M.p); 
	std::swap(n, M.n);
}

Massiv::Massiv(int *pp, int nn) 
{
    std::cout << "Constrn";
    n = nn;
    p = new int[n];
    for (int i = 0; i<n; i++) p[i] = pp[i]; 
}

void Massiv::print() const
{
    if (p!=nullptr)
    for (int i = 0; i < n; i++) std::cout << p[i] << " ";
    else std::cout<<"Null Object";
    std::cout << "n";
}

int main()
{
    int m[] = { 1, 2, 3, 4, 5, 6, 7 };
    Massiv M1(m, 7);
    
	M1.print();
    
	Massiv M2 = M1; // Massiv M2(M1); Вызывается конструктор копирования
    M2.print();
    M1.p[0] = 1000;
    M1.print(); 
	M2.print();
	
	{
        Massiv M3 = std::move(M1); // Вызов конструктора перемещения
        M3.print();
    }
	// При выходе из блока возникла бы проблема потерянной ссылки (указателя) 
    // или утечки памяти, если бы не было деструктора 
    
	Massiv *pM = new Massiv(M1);// Конструктор копирования 
    delete pM; // Автоматически вызывается деструктор
    
	return 0;
}
Если нет явного конструктора перемещения и копирования, 
то конструктор перемещения создается по умолчанию. 

Если есть явный конструктор копирования, 
то конструктор перемещения не создается, 
вместо него используется этот явный конструктор копирования. 

Конструктор перемещения также можно явно удалить, 
для этого в классе заголовок конструктора объявляется 
с ключевым словом delete без тела, 
например, 
    A( A&& a)=delete;

Деструктор

Деструктор — специальный блок операторов (инструкций), 
служащий для деинициализации объекта (освобождение памяти, закрытие файлов и т.п.).

Вызывается автоматически при удаление объекта, 
например, оператором delete или при выходе из блока, 
в котором существует объект. Не имеет возвращаемого значения и параметров. 

Может определяться как внутри класса, так и за пределами.
~имя_класса() {
    тело_деструктора 
}

4. Статические компоненты класса, назначение, пример.

Статические поля
Память под обычные поля (нестатические) выделяется при создание объекта, каждый
объект класса имеет свои копии обычных полей.

Статические поля объявляются в классе с модификатором static, 
память под статические поля выделяется при определении класса, 
и не выделяется при создание объектов. 

Статические поля существуют в единственном экземпляре независимо от того, 
сколько объектов создано, т.е. это поля класса, они общие для всех объектов. 

К статическим полям за пределами класса можно обращаться через имя объекта, 
как к обычным полям, но чаще к ним обращаются через имя класса 
(это можно делать даже когда объекты класса не созданы). 

Статические поля обязательно требуют инициализации (за пределами класса).
Статические методы
На статические поля распространяются модификаторы статуса доступа. 

Для доступа к собственным (private) 
или защищенным (protected) 
статическим полям за пределами класса служат открытые статические методы, 
которые определяются с модификатором static.

К статическим методам за пределами класса обращаются 
как к статическим полям через имя объекта 
или чаще через имя класса. 

Внутри статических методов можно обращаться только 
к статическим полям 
или вызывать другие статические методы. 

К обычным полям и методам обращаться нельзя, 
так как при вызове статического метода объекты могут быть еще не созданы 
(статический метод относится ко всему классу).
#include <iostream>
using namespace std;
class A{
    static int N; // Счетчик созданных объектов
public:
    A()
    {
        N++;
    }
    ~A() { N--; }
    static int getN();
};
int A::getN() 
    {
        return N;
    }
int A::N=0; // Обязательная инициализация статич поля

int main(int argc, char* argv[]) {
    cout << "N=" << A::getN(); 
	A a1, a2, a3, a4; 
	cout<<"nN="<< a1.getN(); 
	{
    A a5; 
	cout << "nN="<< a5.getN();
    }
cout << "nN="<< A::getN();
return 0;
}

5. Указатель this, примеры использования.

Указатель this
Иногда при вызове нестатического метода требуется обращаться к адресу объекта, 
для которого вызывается метод. Но когда программист пишет код метода класса, 
объектов еще не существует. 

Для этих целей любой нестатический метод и конструкторы (деструктор) 
неявно (по умолчанию) получает указатель `this` – указатель на объект класса, 
для которого вызывается метод.
Первый случай использования this

class point { 
    int x,y; 
public: 
    point(int x, int y) 
    {
        this->x=x;   
        this->y=y; 
    }
…..
};
Второй случай использования this

class List // Линейный односвязный список 
{
    int x; // инф. поле 
    List *pNext; // Указатель на след. элемент 
public: 
    List(int x) 
    {
        this->x=x;
    }
    void add(List *&pF) // Добавить элемент в начало списка 
    {
        pNext=pF; pF=this; // Текущий элемент будет первым
    }
…………….
};

6. Указатели на компоненты класса (на функции и поля класса), назначение, пример.

Указатели на компоненты класса
Операции `.*` и `->*` предназначены для работы с указателями на компоненты класса.
До использования указателя его необходимо  соответствующим образом настроить.
Указатели на компонентные функции (методы)

Формат определения указателя на функцию:

<тип_возвр_значения>  (<имя_класса>::*<имя_указателя>)(<спецификация_форм_параметров>);

Настройка указателя:

<имя указателя>=  &<имя_класса>::<имя_функции>;

Вызов функции по указателю:

(<имя_объекта>.*<имя_указателя>)(<параметры>); 
(<имя_ук_на_об>->*<имя_указателя>)(<параметры>);
Указатели на поля

Формат определения указателя на поле класса:

<тип_поля> (<имя_класса>::*<имя_указателя>);

Настройка указателя:

<имя указателя>= &<имя_класса>::<имя_поля>;

Обращение к полю по указателю:

<имя_объекта>.*<имя_указателя>=10; <имя_ук_на_об>->*<имя_указателя>=10;
#include <iostream>
using namespace std; 
struct point{
	int x, y;   
	void printX() 
	{
        cout<<endl<<"x="<<x;
    }
    void printY() 
	{
        cout<<endl<<"y="<<y;
    }
};

int main(int argc, char* argv[]) {

    void (point::*pF)()=&point::printX; // Указатель на функцию класса 
    int point::*p=&point::x; // Указатель на поле класса
    point p1;
//  p1.x=5; 
//  p1.y=10; 
    p1.*p=5; // Обращение к полю х через указатель 
    p=&point::y; // Указатель настраиваем на поле y 
    p1.*p=10; // Обращение к полю y через указатель 
    (p1.*pF)(); // Вызываем функцию printX через указатель
    pF=&point::printY;// Указатель на функцию класса настраиваем на функцию printY
    (p1.*pF)(); // Вызываем функцию printY через указатель
    return 0;
}

7. Дружественные функции и классы, назначение, пример.

Дружественные функции класса
Особенности ДФ: 
	- ДФ не получает ук-ль this, т.к. она не принадлежит классу;
	- Дружественная функция получает доступ к объекту через формальный параметр, 
		объект класса передается по указателю или по ссылке; 
	- Место размещение прототипа ДФ внутри класса безразлично, 
		на нее не распространяется действие модификаторов доступа; 
	- ДФ может быть компонентной функцией другого класса; 
	- ДФ может быть дружественной по отношению к нескольким классам.
Дружественные классы 
	class C1 { friend class C2; ...  }; 
	class C2 {  ....  }
Все функции класса С2 являются дружественными по отношению к классу С1.

Пример:

#include <iostream>

using namespace std;

class point
{
	int x, y;
	public:
	point(int a, int b) {
		x=a; y=b;
	}
	friend void print(point &p); // Заголовок ДФ внутри класса 
	// friend void A::fun(point &p); // Если ДФ принадлежит классу A
};

void print(point &p) // Дружественная функция
{
	cout<<endl<<"x="<< p.x<<" y="<<p.y; // Имеет доступ к закрытым полям (private)
}

int main(int argc, char* argv[]) {
	point p1(3, 5); print(p1); // Вызов ДФ
	return 0;
}

Пример с семинара

#include <iostream>

using namespace std;
class MyVec
{
    int *p=nullptr;
    int n=0;
public:
    MyVec(): p(nullptr) {
        cout<<"MyVec()"<<endl;
    }
    MyVec(int n): n(n)
    {
        p=new int[n];
        cout<<"MyVec(int n)"<<endl;
    }
    MyVec(int *p, int n){
        this->n=n;
        this->p=new int[n];
        for(int i=0; i<n; i++)
            this->p[i]=p[i];
        cout<<"MyVec(int *p, int n)"<<endl;
    }
    MyVec(const MyVec & ob)
    {
        this->n=ob.n;
        this->p=new int[n];
        for(int i=0; i<n; i++)
            this->p[i]=ob.p[i];
        cout<<"MyVec(const MyVec & ob)"<<endl;
    }
    MyVec(MyVec && ob)
    {
        swap(p, ob.p);
        swap(n, ob.n);
        cout<<"MyVec(MyVec && ob)"<<endl;
    }
    MyVec& operator=(const MyVec & ob2)
    {
        if (&ob2 != this)
        {
        if (n<ob2.n)
        {
            delete [] p;
            p=new int [ob2.n];
        }
        n=ob2.n;
        for(int i=0; i<n; i++)
            p[i]=ob2.p[i];
        }
        cout<<"MyVec& operator=(const MyVec & ob2)"<<endl;
        return *this;
    }
    MyVec& operator=(MyVec && ob2)
    {

        swap(p, ob2.p);
        swap(n, ob2.n);

        cout<<"MyVec& operator=(MyVec && ob2)"<<endl;
        return *this;
    }
    ~MyVec()
    {
        delete []p;
        cout<<"~MyVec()"<<endl;
    }
    void print(ostream & out=cout)
    {
        for(int i=0; i<n; i++)
            out<<p[i]<<' ';
        out<<endl;
    }
    MyVec & operator++()
    {
        for(int i=0; i<n; i++) ++p[i];
        return *this;
    }
    MyVec operator++(int)
    {
        MyVec ob(*this);
        for(int i=0; i<n; i++) ++p[i];
        return ob;

    }
    friend ostream & operator<<(ostream & out, const MyVec & ob);
    friend MyVec operator+(const MyVec & ob1, const MyVec & ob2);
};

ostream & operator<<(ostream & out, const MyVec & ob)
{
    for(int i=0; i<ob.n; i++)
        out<<ob.p[i]<<' ';
    out<<endl;
    return out;
}

MyVec operator+(const MyVec & ob1, const MyVec & ob2)
{
    MyVec ob(ob1.n+ob2.n);
    for(int i=0; i<ob1.n; i++)
        ob.p[i]=ob1.p[i];
    for(int i=0; i<ob2.n; i++)
        ob.p[i+ob1.n]=ob2.p[i];
    return ob;
}

MyVec CreateVec()
{
    int m1[]={1, 2, 3, 4, 5};
    MyVec V(m1, 5);
    return V;
}

int main()
{
    /*int m1[]={1, 2, 3, 4, 5};
    MyVec V1(m1, 5);
    MyVec  V2=V1;
    MyVec V3;
    V3=V1+V2;
    cout<<V3;*/
    MyVec V=CreateVec();
    cout<<V;
    return 0;
}

8. Шаблоны классов в Си++. Примеры использования.

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

template <список_параметров_шаблона> <определение_шаблона_класса>

Имя параметра шаблона – это имя неизвестного заранее типа.

При создании объекта указывается имя конкретного типа и по шаблону генерируется класс.

Существует библиотека стандартных шаблонов (STL- Standard Template Library), вошла в стандарт С++ 11 В ней шаблонами задаются контейнеры – предназначенные для хранение набора объектов в памяти (объекты могут быть любых типов).
Контейнеры: vector, list, set, stack, queue.

#include <iostream>
using namespace std;
template <class T> // Шаблон класса вектор (массив) 
		           // T - тип элементов вектора
class vektor
{   
	T* data;// Указатель на массив 
	int size;// Размерность
public:
    vektor(int n=10); 
	~vektor() { delete [] data; }
    T& operator[] (int i) // Оператор функция возвращает элемент по индексу
    {
        return data[i];
    }
    T getSum();  // Функция считает сумму элементов
};

// Внешнее определение конструктора 
template <class T> // Перед любым определением метода за пределами класса
vektor<T>::vektor(int n) 
{
    data=new T[n]; 
	size=n;
}

// Внешнее определение функции 
template <class T> // Перед любым определением метода за пределами класса
T vektor<T>::getSum() 
{
    T sum=0; 
	for(int i=0; i<size; i++) 
		sum+=data[i];
    return sum;
}
int main() 
{
    vektor<int> X(5); // Вектор элементов типа int X[1]=101;// Вызов оператор функции
    cout<<"x[1]="<< X[1];
    vektor<double> Y(10); // Создается класс вместо Т подставляется double Y[0]=10.51;// Вызов оператор функции
    cout << "nY[0]=" << Y[0]; 
	for(int i=0; i<10; i++) Y[i]=i; 
	cout << "nSum="<< Y.getSum();
    return 0;
}

Примечание.
Вместо ключевого слова class можно перед параметром шаблона использовать ключевое слово typename

template <typename T> // Шаблон класса вектор (массив) 
                      // T - тип элементов вектора
class vektor
{
    ….
};

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

Правило трёх 
(также известное как «Закон Большой Тройки» или «Большая Тройка») - правило в C++, 
гласящее, что если класс или структура определяет один из следующих методов, 
то они должны явным образом определить все три метода: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 

С выходом одиннадцатого стандарта правило расширилось 
и теперь называется правило пяти. 
Теперь при реализации конструктора необходимо реализовать: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 
	• Конструктор перемещения 
	• Оператор присваивания перемещением
Перегрузка операций в языке Си++ 
это возможность распространения действия стандартных операций на операнды, 
для которых эти операции первоначально не предназначались. 
Это возможно, если хотя бы один из операндов является объектом класса, 
для этого создается специальная, так называемая, 
оператор - функция, которая может быть как членом класса, 
так и функцией, не принадлежащей классу.

Формат определения оператор- функции имеет вид:
	<тип_возвращаемого_значения> 
	operator <знак_операции> 
	(спецификация_параметров) 
	{
		операторы_тела_функции
	}

Существует три способа перегрузки: 
	- оператор-функция определяется как функция, не принадлежащая классу; 
	- оператор-функция определяется как функция класса;
	- оператор-функция определяется как дружественная функция класса.

Особенности перегрузки операций: 
	- можно перегружать только стандартные операции, 
		например, нельзя перегрузить операцию ‘**’ 
		(возведение в степень в языке Фортран, отсутствует в Си++);
	- не допускают перегрузки операции: ‘.’, ‘.*’, ‘?:’, ‘::’, ‘sizeof’, ‘#’, ‘##’;
	- при перегрузке сохраняется арность операций 
		(унарная операция остается унарной, а бинарная – бинарной); 
	- бинарная операция перегружается либо как  функция, 
		не принадлежащая классу с двумя параметрами, 
		один обязательно объект (ссылка на объект) класса, 
		или как функция класса с одним параметром, 
		первым операндом операции выступает объект класса, 
		для которого вызывается функция; 
	- бинарные операции ‘=‘, ‘[]’, ‘->’ 
		должны обязательно определяться как компонентные функции класса; 
	- унарная операция перегружается либо как  функция, 
		не принадлежащая классу с одним параметром - объектом (ссылкой на объект) класса, 
		или как функция класса без параметров, 
		операндом операции выступает объект класса, 
		для которого вызывается функция.

.h

#include <string>
#include <vector>
#include <iostream>
#include <fstream>

using namespace std;

#ifndef FIREWALL_H
#define FIREWALL_H

class Vector // Класс - вектор
{
	double *point = nullptr; // Указатель на массив (вектор)
	int n = 0; // Размерность вектора (число элементов) массива
public:
	Vector(); // Конструктор без параметров, задает "пустой" объект
	Vector(double *p, int n); // Коструктор на входе массив, задающий вектор
	Vector(int n); // Конструктор - выделяем память без инициализации
	Vector(const Vector &V);  // Конструктор копирования
	Vector(Vector &&V);  // Параметр - правосторонняя ссылка
	double &operator[](int index); // Оператор - функция (перегрузка операции обращения к элементу)
	Vector &operator=(const Vector &v2); // Оператор- функция копирования объекта
	Vector &operator=(Vector &&v2); // Оператор- функция перемещения объекта
	~Vector(); // Деструктор

	friend double operator*(Vector &v1, Vector &v2); // Дружественная функция,
	friend Vector operator+(const Vector &v1, const Vector &v2); // определенная вне класса

	friend istream& operator>>(std::istream& in, Vector& vec);
	friend ostream& operator<<(std::ostream& out, const Vector& vec);

	void print(ostream& out = cout) const; // Печать вектора (массива), заменить на перегрузку <<
};

#endif //FIREWALL_H

.cpp

#include "Vector.h"
#include <string>
#include <iostream>
#include <fstream>

using namespace std;

Vector::Vector() // Конструктор без параметров, задает "пустой" объект
{
	point = nullptr; // Указатель на массив (вектор)
	n = 0; // Размерность вектора (число элементов) массива
	cout << "Vector()" << endl;
}

Vector::Vector(double *p, int n) // Коструктор на входе массив, задающий вектор
{
	this->n = n; // Задаем число элементов
	this->point = new double[n]; // Выделяем память
	for (int i = 0; i < n; i++) this->point[i] = p[i]; // Копируем один массив в другой
	cout << "Vector(double *p, int n)" << endl;
}

Vector::Vector(int n) : n(n) // Конструктор - выделяем память без инициализации
{
	point = new double[n];
	cout << "Vector(int n)" << endl;
}

Vector::Vector(const Vector &V) { // Конструктор копирования
	n = V.n;
	point = new double[n];
	for (int i = 0; i < n; i++) point[i] = V.point[i];
	cout << "Vector(const Vector & V) n=" << n << endl;
}

void Vector::print(ostream& out) const
{
	for (int i = 0; i < n; i++)
		cout << point[i] << " ";
	cout << endl << endl;
}

Vector::Vector(Vector &&V)  // Параметр - правосторонняя ссылка
{
	std::swap(point, V.point);
	std::swap(n, V.n);
	cout << "Vector(Vector &&V)" << endl;
}

double &Vector::operator[](int index) // Оператор - функция (перегрузка операции обращения к элементу)
{
	return point[index];
}

Vector &Vector::operator=(const Vector &v2) // Оператор- функция копирования объекта
{
	if (this != &v2) // Запрет копирования вектора самого в себя
	{
		n = v2.n;
		if (point != nullptr) delete[] point; // Освобождаем память старого вектора
		point = new double[n]; // Выделяем память для нового вектора
		for (int i = 0; i < n; i++) point[i] = v2.point[i];
	}
	cout << "Vector & operator = (const Vector& v2)" << endl;
	return *this; // Возвращаем ссылку на текущий объект
}

Vector &Vector::operator=(Vector &&v2) // Оператор - функция перемещения объекта
{
	if (this != &v2) // Запрет перемещения вектора самого в себя
	{
		std::swap(point, v2.point);
		std::swap(n, v2.n);
	}
	cout << "Vector & operator = (Vector&& v2)" << endl;
	return *this; // Возвращаем ссылку на текущий объект
}

Vector::~Vector() // Деструктор
{
	cout << "~Vector() n=" << n << endl;
	if (point != nullptr) delete[] point; // Освобождаем память
}

// Умножение числа на вектор (первый операнд не объект класса,
// функция обязательно определяется вне класса)
double operator *(Vector &v1, Vector& v2) // Оператор - функция вне класса
{
	double Res = 0;
	for (int i = 0; i < v2.n; i++) Res += v1.point[i] * v2.point[i]; // Заполняем массив
	return Res; // Возвращаем объект
}
Vector operator +(const Vector& v1, const Vector& v2) // Оператор - функция вне класса
{
	Vector V(v1.n + v2.n); // Создаем новый объект заданного размера
	for (int i = 0; i < v1.n; i++) V.point[i] = v1.point[i]; // Заполняем массив
	for (int i = 0; i < v2.n; i++) V.point[i + v1.n] = v2.point[i]; // Заполняем массив
	return V; // Возвращаем объект
}
istream& operator >> (std::istream& in, Vector& vec)
{
	in >> vec.n;
	for (int i = 0; i < vec.n; ++i) {
		in >> vec[i];
	}
	return in;
}
ostream& operator << (std::ostream& out, const Vector& vec) {
	{
		for (int i = 0; i < vec.n; i++)
			out << vec.point[i] << " ";
		out << endl;
	}
	return out;
}

main

#include <iostream>
#include <fstream>
#include "Vector.h"

using namespace std;

int main() {
	ifstream file;
	ofstream fout;
	vector <double> nums;

	file.open("input.txt");
	fout.open("fout.txt");

	if (file.is_open()) {
		double s;
		for (file >> s; !file.eof(); file >> s) { // пример вставки
			nums.push_back(s);
		}

		double* numsD = new double[nums.size()];

		for (int i = 0; i < nums.size(); i++) {
			numsD[i] = nums[i]; // пример обращения к операции []
		}
		int size = nums.size();
		cout << "Array:" << endl;
		Vector v1(numsD, size);
		v1.print(fout);

		cout << "Copying" << endl;
		Vector v2(numsD, size);
		v2 = v1; // пример функции копирования Vector &operator=(const Vector &v2)
		v2.print(fout);

		cout << "v1 * v2 = ";
		double Result = v1 * v2;
		cout << Result << endl << endl;

		cout << "Moving" << endl;
		Vector v4(numsD, size);
		Vector v5(numsD, size);
		v4 = move(v1); //пример функции перемещения
		v4.print(fout);

		cout << "fout" << endl;
		fout << v4;
		std::cout << std::endl;


	}

	file.close();
	fout.close();

	return 0;
}

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

Правило трёх 
(также известное как «Закон Большой Тройки» или «Большая Тройка») - правило в C++, 
гласящее, что если класс или структура определяет один из следующих методов, 
то они должны явным образом определить все три метода: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 

С выходом одиннадцатого стандарта правило расширилось 
и теперь называется правило пяти. 
Теперь при реализации конструктора необходимо реализовать: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 
	• Конструктор перемещения 
	• Оператор присваивания перемещением
Перегрузка операций в языке Си++ 
это возможность распространения действия стандартных операций на операнды, 
для которых эти операции первоначально не предназначались. 
Это возможно, если хотя бы один из операндов является объектом класса, 
для этого создается специальная, так называемая, 
оператор - функция, которая может быть как членом класса, 
так и функцией, не принадлежащей классу.

Формат определения оператор- функции имеет вид:
	<тип_возвращаемого_значения> 
	operator <знак_операции> 
	(спецификация_параметров) 
	{
		операторы_тела_функции
	}

Существует три способа перегрузки: 
	- оператор-функция определяется как функция, не принадлежащая классу; 
	- оператор-функция определяется как функция класса;
	- оператор-функция определяется как дружественная функция класса.

Особенности перегрузки операций: 
	- можно перегружать только стандартные операции, 
		например, нельзя перегрузить операцию ‘**’ 
		(возведение в степень в языке Фортран, отсутствует в Си++);
	- не допускают перегрузки операции: ‘.’, ‘.*’, ‘?:’, ‘::’, ‘sizeof’, ‘#’, ‘##’;
	- при перегрузке сохраняется арность операций 
		(унарная операция остается унарной, а бинарная – бинарной); 
	- бинарная операция перегружается либо как  функция, 
		не принадлежащая классу с двумя параметрами, 
		один обязательно объект (ссылка на объект) класса, 
		или как функция класса с одним параметром, 
		первым операндом операции выступает объект класса, 
		для которого вызывается функция; 
	- бинарные операции ‘=‘, ‘[]’, ‘->’ 
		должны обязательно определяться как компонентные функции класса; 
	- унарная операция перегружается либо как  функция, 
		не принадлежащая классу с одним параметром - объектом (ссылкой на объект) класса, 
		или как функция класса без параметров, 
		операндом операции выступает объект класса, 
		для которого вызывается функция.

.h

#include <string>
#include <vector>
#include <iostream>
#include <fstream>

using namespace std;

#ifndef FIREWALL_H
#define FIREWALL_H

class Vector // Класс - вектор
{
	double *point = nullptr; // Указатель на массив (вектор)
	int n = 0; // Размерность вектора (число элементов) массива
public:
	Vector(); // Конструктор без параметров, задает "пустой" объект
	Vector(double *p, int n); // Коструктор на входе массив, задающий вектор
	Vector(int n); // Конструктор - выделяем память без инициализации
	Vector(const Vector &V);  // Конструктор копирования
	Vector(Vector &&V);  // Параметр - правосторонняя ссылка
	double &operator[](int index); // Оператор - функция (перегрузка операции обращения к элементу)
	Vector &operator=(const Vector &v2); // Оператор- функция копирования объекта
	Vector &operator=(Vector &&v2); // Оператор- функция перемещения объекта
	~Vector(); // Деструктор

	friend double operator*(Vector &v1, Vector &v2); // Дружественная функция,
	friend Vector operator+(const Vector &v1, const Vector &v2); // определенная вне класса

	friend istream& operator>>(std::istream& in, Vector& vec);
	friend ostream& operator<<(std::ostream& out, const Vector& vec);

	void print(ostream& out = cout) const; // Печать вектора (массива), заменить на перегрузку <<
};

#endif //FIREWALL_H

.cpp

#include "Vector.h"
#include <string>
#include <iostream>
#include <fstream>

using namespace std;

Vector::Vector() // Конструктор без параметров, задает "пустой" объект
{
	point = nullptr; // Указатель на массив (вектор)
	n = 0; // Размерность вектора (число элементов) массива
	cout << "Vector()" << endl;
}

Vector::Vector(double *p, int n) // Коструктор на входе массив, задающий вектор
{
	this->n = n; // Задаем число элементов
	this->point = new double[n]; // Выделяем память
	for (int i = 0; i < n; i++) this->point[i] = p[i]; // Копируем один массив в другой
	cout << "Vector(double *p, int n)" << endl;
}

Vector::Vector(int n) : n(n) // Конструктор - выделяем память без инициализации
{
	point = new double[n];
	cout << "Vector(int n)" << endl;
}

Vector::Vector(const Vector &V) { // Конструктор копирования
	n = V.n;
	point = new double[n];
	for (int i = 0; i < n; i++) point[i] = V.point[i];
	cout << "Vector(const Vector & V) n=" << n << endl;
}

void Vector::print(ostream& out) const
{
	for (int i = 0; i < n; i++)
		cout << point[i] << " ";
	cout << endl << endl;
}

Vector::Vector(Vector &&V)  // Параметр - правосторонняя ссылка
{
	std::swap(point, V.point);
	std::swap(n, V.n);
	cout << "Vector(Vector &&V)" << endl;
}

double &Vector::operator[](int index) // Оператор - функция (перегрузка операции обращения к элементу)
{
	return point[index];
}

Vector &Vector::operator=(const Vector &v2) // Оператор- функция копирования объекта
{
	if (this != &v2) // Запрет копирования вектора самого в себя
	{
		n = v2.n;
		if (point != nullptr) delete[] point; // Освобождаем память старого вектора
		point = new double[n]; // Выделяем память для нового вектора
		for (int i = 0; i < n; i++) point[i] = v2.point[i];
	}
	cout << "Vector & operator = (const Vector& v2)" << endl;
	return *this; // Возвращаем ссылку на текущий объект
}

Vector &Vector::operator=(Vector &&v2) // Оператор - функция перемещения объекта
{
	if (this != &v2) // Запрет перемещения вектора самого в себя
	{
		std::swap(point, v2.point);
		std::swap(n, v2.n);
	}
	cout << "Vector & operator = (Vector&& v2)" << endl;
	return *this; // Возвращаем ссылку на текущий объект
}

Vector::~Vector() // Деструктор
{
	cout << "~Vector() n=" << n << endl;
	if (point != nullptr) delete[] point; // Освобождаем память
}

// Умножение числа на вектор (первый операнд не объект класса,
// функция обязательно определяется вне класса)
double operator *(Vector &v1, Vector& v2) // Оператор - функция вне класса
{
	double Res = 0;
	for (int i = 0; i < v2.n; i++) Res += v1.point[i] * v2.point[i]; // Заполняем массив
	return Res; // Возвращаем объект
}
Vector operator +(const Vector& v1, const Vector& v2) // Оператор - функция вне класса
{
	Vector V(v1.n + v2.n); // Создаем новый объект заданного размера
	for (int i = 0; i < v1.n; i++) V.point[i] = v1.point[i]; // Заполняем массив
	for (int i = 0; i < v2.n; i++) V.point[i + v1.n] = v2.point[i]; // Заполняем массив
	return V; // Возвращаем объект
}
istream& operator >> (std::istream& in, Vector& vec)
{
	in >> vec.n;
	for (int i = 0; i < vec.n; ++i) {
		in >> vec[i];
	}
	return in;
}
ostream& operator << (std::ostream& out, const Vector& vec) {
	{
		for (int i = 0; i < vec.n; i++)
			out << vec.point[i] << " ";
		out << endl;
	}
	return out;
}

main

#include <iostream>
#include <fstream>
#include "Vector.h"

using namespace std;

int main() {
	ifstream file;
	ofstream fout;
	vector <double> nums;

	file.open("input.txt");
	fout.open("fout.txt");

	if (file.is_open()) {
		double s;
		for (file >> s; !file.eof(); file >> s) { // пример вставки
			nums.push_back(s);
		}

		double* numsD = new double[nums.size()];

		for (int i = 0; i < nums.size(); i++) {
			numsD[i] = nums[i]; // пример обращения к операции []
		}
		int size = nums.size();
		cout << "Array:" << endl;
		Vector v1(numsD, size);
		v1.print(fout);

		cout << "Copying" << endl;
		Vector v2(numsD, size);
		v2 = v1; // пример функции копирования Vector &operator=(const Vector &v2)
		v2.print(fout);

		cout << "v1 * v2 = ";
		double Result = v1 * v2;
		cout << Result << endl << endl;

		cout << "Moving" << endl;
		Vector v4(numsD, size);
		Vector v5(numsD, size);
		v4 = move(v1); //пример функции перемещения
		v4.print(fout);

		cout << "fout" << endl;
		fout << v4;
		std::cout << std::endl;


	}

	file.close();
	fout.close();

	return 0;
}

11. Особенности перегрузки операций ++ и – в префиксной и постфиксной формах.

Правило трёх 
(также известное как «Закон Большой Тройки» или «Большая Тройка») - правило в C++, 
гласящее, что если класс или структура определяет один из следующих методов, 
то они должны явным образом определить все три метода: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 

С выходом одиннадцатого стандарта правило расширилось 
и теперь называется правило пяти. 
Теперь при реализации конструктора необходимо реализовать: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 
	• Конструктор перемещения 
	• Оператор присваивания перемещением
Перегрузка операций `++` и `--`,
чтобы отличить `постфиксную` форму от `префиксной`,
вводится формальный параметр типа `int`.
Пример объявления операторов-функций внутри класса:

Vector & operator++()  // Префиксная форма операции ++ 
Vector operator++(int) // Постфиксная форма операции ++ 
                       // всегда есть формальный (фиктивный)
                       // параметр типа int
                       
Пример объявления операторов-функций за пределами класса:

Vector & operator++(Vector & V) // Префиксная форма операции ++ 
Vector operator++(Vector & V, int)  // Постфиксная форма операции ++ 
                                    // всегда есть формальный 
                                    // (фиктивный) параметр типа int
                                    
В постфиксной форме вначале с помощью конструктора копирования создаем новый объект,
в котором сохраняется начальное (старое) состояние текущего объекта,
затем текущий объект изменяется, и возвращается созданный объект.

12. Особенности перемещения объекта, продемонстрировать пример перемещения при перегрузке операции «=» (присваивание) на примере своего класса вектор, содержащем в качестве поля динамический массив элементов типа int.

Правило трёх 
(также известное как «Закон Большой Тройки» или «Большая Тройка») - правило в C++, 
гласящее, что если класс или структура определяет один из следующих методов, 
то они должны явным образом определить все три метода: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 

С выходом одиннадцатого стандарта правило расширилось 
и теперь называется правило пяти. 
Теперь при реализации конструктора необходимо реализовать: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 
	• Конструктор перемещения 
	• Оператор присваивания перемещением
По аналогии с конструкторами копирования и перемещения 
по умолчанию существуют в классе операторы 
присваивания с копированием и присваивания с перемещением. 
Правила создания и использования аналогичные. 
Эти операторы можно переопределять или удалять, 

например,
	A & operator=(const A & Ob) = delete; 

	A & operator=(A && Ob) = delete;
#include <iostream>

using namespace std;
class Vector
{
	int *p=nullptr; 
	int n=0; 
	
public:
	Vector() // "Пустой" объект
	{
		n = 0;
		p = nullptr;
	}
	
	~Vector() { 
		if (p != nullptr) delete[] p;
		cout <<  "Destructor"<<endl; 
	} // Деструктор 
	
	Vector(int *p, int n) // Создаем вектор на основе обычного массива
	{
		this->n = n;
		this->p = new int[n];
		for (int i = 0; i<n; i++) this->p[i] = p[i]; 
	}
	
	Vector(int n): n(n) // Выделяем память без инициализации
	{
		p=new int[n];
	}
	
	void print() const // Функция печати
	{
		if (p!=nullptr) for (int i = 0; i<n; i++)
			cout << p[i] << ' ';
		else cout << "Empty";
		cout << endl;
	}
	
	// Перегрузка операции обращение по индексу 
	int & operator[](int index) // ссылка позволяет изменять значение элемента
	{
		return p[index]; // Возвращаем элемент по индексу
	}
	
	int operator[](int index) const // Изменять значение нельзя
	{
		return p[index]; // Возвращаем элемент по индексу
	}
	
	Vector & operator++() // Префиксная форма операции ++
	{
		for (int i = 0; i < n; i++) ++p[i];
		return *this;
	}
	
	Vector operator++(int)  // Постфиксная форма операции ++ 
					// всегда есть формальный (фиктивный) параметр типа int
	{
		Vector temp (*this); // Создаем новый объект (старое состояние текущего объекта) 
		for (int i = 0; i < n; i++) ++p[i]; // Инкремент текущего объекта 
		return temp; // Возвращаем старое состояние объекта
	}
	
	Vector(const Vector & V) // Конструктор копирования
	{
		// Задаем новый объект
		n = V.n;
		p = new int[n];
		for (int i = 0; i<n; i++) p[i] = V.p[i];
		cout << "Constructor Copy"<<endl;
	}
	
	Vector(Vector && V) // Конструктор перемещения
	{
		// Короткая форма
		swap(p, V.p); swap(n, V.n);
		cout << "Constructor Move"<<endl;
	}
	
	Vector & operator=(const Vector & V2) // Оператор присваивания с копированием, 
	// параметр второй операнд, первый операнд - объект текущего класса
	{
		if (this!=&V2) // Запрет копирования объекта самого в себя
		{
			if (p != nullptr) delete[] p; // Очищает (удаляем) старый объект,если он есть 
			// Задаем новый объект
			n = V2.n;
			p = new int[n];
			for (int i = 0; i<n; i++) p[i] = V2.p[i]; 
		}
			cout << "operator= copy"<<endl; 
			return *this; // Возвращаем текущий объект
	}
	
	Vector & operator=(Vector && V2) // Оператарор присваивания с перемещением, 
		//Параметр второй операнд, первый операнд - объект текущего класса
	{
		if (this!=&V2) // Запрет перемещения объекта самого в себя
		{
		/*  // Все делаем явно 
		if (p != nullptr) delete[] p; // Очищает (удаляем) старый объект,если он есть
		// Задаем новый объект 
		n = V2.n; 
		p = V2.p; V2.n = 0; 
		V2.p = nullptr;
		*/ 
		
		// Короткая форма
		swap(p, V2.p); 
		swap(n, V2.n);
		}
	cout << "operator= move"<<endl; 
	return *this; // Возвращаем текущий объект
	}
	
	// Умножение вектора на число
	// на выходе новый вектор
	Vector operator*(int x) const
	{
		Vector V(n);
		for (int i = 0; i<n; i++) V[i] = p[i] * x;
		return V;
	}

	friend Vector operator*(int x, const Vector& v2); 
	friend double operator*(const double *p1, const Vector &Ob2); 

	// Чтобы получить доступ к закрытым полям класса 
	// friend Vector & operator++(Vector & V); 
	// Префиксная форма операции ++ 
	// friend Vector operator++(Vector & V, int);  
	// Постфиксная форма операции ++ всегда есть формальный (фиктивный) параметр типа int
	};

	// Умножение числа на вектор, так как 1 операнд не объект класса, 
	// то оператор - функция не принадлежит классу
	Vector operator*(int x, const Vector& v2)
	{
		Vector V(v2.n);
		for (int i = 0; i<v2.n; i++)
		V[i] = x*v2[i];
		return V;
	}

	// Скалярное произведение векторов 
	// Оператор-функция обязательно не принадлежит классу, 
	// Так как первый операнд не объект класса
	double operator*(const double *p1, const Vector &Ob2)
	{
		double sum = 0;
		for (int i = 0; i<Ob2.n; i++)
			sum += p1[i] * Ob2.p[i];
		return sum;
	}
	
	// Пример перегрузки операторов ++ вне класса 
	/*
	Vector & operator++(Vector & V) // Префиксная форма операции ++ 
	{
		for (int i = 0; i < V.n; i++) ++V.p[i]; 
		return V;
	}
	
	Vector operator++(Vector & V, int)  // Постфиксная форма операции ++ 
		// всегда есть формальный (фиктивный) параметр типа int 
	{
		Vector temp(V); // Создаем новый объект (старое состояние текущего объекта) 
		for (int i = 0; i < V.n; i++) ++V.p[i]; // Инкремент текущего объекта 
		return temp; // Возвращаем старое состояние объекта 
	}
	*/
	
	int main()
	{
	int m1[] = { 1, 2, 3, 4, 5 };
	Vector V1(m1, 5); // Создаем объект на основе обычного массива
	V1.print();
	V1[0] = 100;
	V1.print();
	
	Vector V2;
	V2 = 2 * V1; // V2=operator*(2, V1);
	V2.print();
	
	Vector V3;
	//V3 = move(V1++); // Чтобы не создавать лишние объекты используем оператор перемещения 
	V3 = V1++; // Здесь тоже вызывается оператор = с перемещением Чтобы не создавать лишние объекты
	
	V1.print(); 
	V3.print();
	return 1;
	}

13. Наследование классов. Объявление производного класса, пример использования.

Общие сведения о наследовании Основная идея:

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

Примеры:
class S: X, Y, Z { ... } ; 

class B: public A {….}; 

class D: public X, protected B 
{….};
Перед именем базового класса может указываться
статус доступа наследования или тип наследования доступа
(одно из ключевых слов public, protected, private).

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

Производный класс может определяться с ключевым словом struct или class
(с ключевым словом union производный класс не определяется).

Если производный класс определен с ключевым словом class,
то по умолчанию статус доступа наследования private.

Если производный класс определен с ключевым словом struct,
то по умолчанию статус доступа наследования public.

image

Особенности конструкторов при наследовании 

Конструктор производного класса в первую очередь всегда 
должен вызывать конструктор базового класса. 

Если это действие не выполняется явно, 
то по умолчанию вызывается конструктор без параметров 
(если он есть, если его нет, будет ошибка). 

Если класс имеет несколько базовых, 
то конструкторы базовых классов должны вызываться в порядке 
перечисления этих классов в списке базовых.


#include <cstdlib> 
#include <iostream>

using namespace std; 

class A
{
public:
	A()
	{
		cout<<endl<<"A1";
	}
	
	A(int x) {
		cout<<endl<<"A2";
	}
};
	
class B: public A
{
public:
	B(): A(4) // Явный вызов конструктора с 1-м параметром, 
	// если нет явного вызова, то вызывается конструктор без параметров, 
	// если его нет, будет ошибка
	{
		cout<<endl<<"B";
	}
};

class C
{
public:
	/*
	C()
	{
		cout<<endl<<"C1";
	}
	*/
	
	C(int x) {
		cout<<endl<<"C2";
	}
};
	
class D: public A, public C
{
public:
	D() : A(3), C(5) // Явный вызов конструкторов
	{
		cout<<endl<<"D";
	}
};

int main(int argc, char* argv[]) 
{
	B b1; 
	D d1; 
	
	system("pause");
	return 0;
}
Особенности деструкторов при наследовании 
Деструктор производного класса всегда 
неявно по умолчанию после выполнения своего тела вызывает деструкторы базовых классов. 


Причем порядок разрушения объекта (вызовов деструкторов) 
обратен порядку создания (вызова конструкторов).


#include <iostream>

using namespace std; 

class A
{
public:
	~A()
	{
		cout<<endl<<"A";
	}
};

class B
{
	public:
	~B()
	{
		cout<<endl<<"B";
	}
};

class C: public A, public B
{
	public:
	~C()
	{
		cout<<endl<<"C";
	}
};

int main(int argc, char* argv[]) 
{

	{
		C c1;
	} // При выходе из блока объект с1 уничтожается - вызывается деструктор
	
	return 0;
}

14. Статусы доступа при наследовании классов.

См. вопрос 13

15. Конструкторы и деструктор в производных классах, примеры.

Начнем с того, что когда мы создаем элементы (переменные) класса, мы не можем присвоить им значения в самом определении класса. Компилятор выдаст ошибку. Поэтому нам необходимо создавать отдельный метод (так называемую set-функцию) класса, с помощью которого и будет происходить инициализация элементов. При этом, если необходимо создать, к примеру, 20 объектов класса, то чтобы инициализировать элементы потребуется 20 раз вызвать set-функции.

Тут нам как раз сможет помочь конструктор класса. Кстати, конструктор (от слова construct — создавать) – это специальный метод класса, который предназначен для инициализации элементов класса некоторыми начальными значениями.

В отличии от конструктора, деструктор (от слова destruct — разрушать) — специальный метод класса, который служит для уничтожения элементов класса. Чаще всего его используют тогда, когда в конструкторе,при создании объекта класса, динамически был выделен участок памяти и необходимо эту память очистить, если эти значения уже не нужны для дальнейшей работы программы.

Важно запомнить:

конструктор и деструктор, мы всегда объявляем в разделе public;
при объявлении конструктора, тип данных возвращаемого значения не указывается, в том числе — void!!!;
у деструктора также нет типа данных для возвращаемого значения, к тому же деструктору нельзя передавать никаких параметров;
имя класса и конструктора должно быть идентично;
имя деструктора идентично имени конструктора, но с приставкой ~ ;
В классе допустимо создавать несколько конструкторов, если это необходимо. Имена, согласно пункту 2 нашего списка, будут одинаковыми. Компилятор будет их различать по передаваемым параметрам (как при перегрузке функций). Если мы не передаем в конструктор параметры, он считается конструктором по умолчанию;
Обратите внимание на то, что в классе может быть объявлен только один деструктор;
Сразу хочу привести пример, который доступно покажет, как работает конструктор:


# include <iostream>
using namespace std;
 
class AB //класс
{
    private:
    int a;
    int b;
    public:
    AB()    //это конструктор:  1) у конструктора нет типа возвращаемого значения! в том числе void!!!
    //   2) имя должно быть таким как и у класса (в нашем случае AB)
    {
        a = 0;//присвоим начальные значения переменным
        b = 0;
        cout << "Работа конструктора при создании нового объекта: " << endl;//и здесь же их отобразим на экран
        cout << "a = " << a << endl;
        cout << "b = " << b << endl << endl;
    }
 
    void setAB() // с помощью этого метода изменим начальные значения заданные конструктором
    {
        cout << "Введите целое число а: ";
        cin >> a;
        cout << "Введите целое число b: ";
        cin >> b;
    }
 
    void getAB() //выведем на экран измененные значения
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl << endl;
    }
};
 
int main()
{
    setlocale(LC_ALL, "rus");
 
    AB obj1;     //конструктор сработает на данном этапе (во время создания объекта класса)
 
    obj1.setAB();   //присвоим новые значения переменным
    obj1.getAB();   //и выведем их на экран
 
    AB obj2;     //конструктор сработает на данном этапе (во время создания 2-го объекта класса)
return 0;
}

Как видно из результата работы программы, конструктор срабатывает сразу, при создании объектов класса, поэтому, явно вызывать конструктор не нужно, он сам «приходит» :)

Хочется еще добавить, что, как и обычным функциям, мы можем передавать конструктору параметры. Через параметры, конструктору можно передавать любые данные, которые будут необходимы при инициализации объектов класса.

Рассмотрим еще один пример, это все та же программа, только в код внесены некоторые изменения. Тут же покажем принцип работы деструктора:

# include <iostream>
using namespace std;
 
class AB //класс
{
    private:
    int a;
    int b;
 
    public:
    AB(int A, int B) //эти параметры мы передадим при создании объекта в main
    {
        a = A;//присвоим нашим элементам класса значения параметров
        b = B;
        cout << "Тут сработал конструктор, который принимает параметры: " << endl;//и здесь же их отобразим на экран
        cout << "a = " << a << endl;
        cout << "b = " << b << endl << endl;
    }
 
    void setAB()
    {
        cout << "Введите целое число а: ";
        cin >> a;
        cout << "Введите целое число b: ";
        cin >> b;
    }
 
    void getAB()
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl << endl;
    }
 
    ~AB() // это деструктор. не будем заставлять его чистить память, пусть просто покажет где он сработал
    {
        cout << "Тут сработал деструктор" << endl;
    }
};
 
int main()
{
setlocale(LC_ALL, "rus");
 
AB obj1(100, 100);  //передаем конструктору параметры
 
obj1.setAB();   //присвоим новые значения переменным
obj1.getAB();   //и выведем их на экран
 
AB obj2(200, 200);  //передаем конструктору параметры
}
Деструктор срабатывает в тот момент, когда завершается работа программы и уничтожаются все данные. Мы его не вызывали – он сработал сам. Как видно, он сработал 2 раза, так как и конструктор. Уже от себя добавлю, что, в первую очередь, он удалил второй созданный объект (где a = 200, b = 200), а затем первый (где a = 100, b = 100). «Последним пришёл — первым вышел».

16. Множественное наследование и виртуальные базовые классы, примеры.

Множественное наследование и виртуальные базовые классы 
Представление отношения наследования в виде диаграммы классов.

image

Множественное наследование – это когда класс имеет в качестве базовых более одного класса.

image

Пример заголовков классов 

class X { ... public: int k; void f() { ... } ... }; 
class Y: public X { ... } ; 
class Z: public X { ... }; 
class A: public Y, public Z { ... }; 
	При  такой схеме наследования происходит дублирование полей и функций класса X в классе А.
	
A d; Обращение к полям и функциям при дублировании за пределами класса: 
d.A::Y::X::k=5; 
d.A::Y::X::f(); 
d.A::Z::X::k=10; 
d.A::Z::X::f(); 

или

d.Y::k=5; 
d.Y::f(); 
d.Z::k=10; 
d.Z::f(); 


Внутри класса A: 
Y::X::k=5; 
Y::X::f(); 
Z::X::k=5; 
Z::X::f(); 

или 

Y::k=5; 
Y::f(); 
Z::k=5; 
Z::f();
Для устранения возможность дублирования полей и методов, 
когда они наследуются из 1-го класса при множественном наследовании, 
введено понятие виртуального класса, 
в предыдущем примере класс Х должен быть объявлен как виртуальный, 
для этого используется ключевое слово virtual.

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

(Для обычного наследования в Си++ в оперативной памяти 
в объекте производного класса вначале идут поля базового класса, 
а затем поля производного, 

при виртуальном наследовании дополнительно появляется ссылка в производном классе).

Термин «виртуальный» собственно относится не к базовому классу, а к способу наследования от него. 
class X { ... public: int k; void f() { ... } ... }; 
class Y: virtual public X { ... } ; 
class Z: virtual public X { ... }; 
class A: public Y, public Z { ... }; 

17. Переопределение функций при наследовании классов, виртуальные функции в Си++ (таблица виртуальных функций). Статическое и динамическое связывание. Примеры.

18. Абстрактные и локальные классы в Си++, примеры.

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

Что такое чистые виртуальные функции (pure virtual functions)? Это функции, которые не имеют определения. Чтобы определить виртуальную функцию как чистую, ее объявление завершается значением "=0". Например, определим абстрактный класс, который представляет геометрическую фигуру:


class Figure
{
public:
    virtual double getSquare() = 0;
    virtual double getPerimeter() = 0;
    virtual void showFigureType() = 0;
};
Класс Figure является абтрактным, потому он содержит как минимум одну чистую виртуальную функцию. А в данном случае даже три таких функции. И ни одна из функций не имеет никакой реализации. Реализацию должны определять классы-наследники.

При этом мы не можем создать объект абстрактного класса:

1
Figure figure;
Определим следующую программу:


#include <iostream>
 
class Figure
{
public:
    virtual double getSquare() =0;
    virtual double getPerimeter() =0;
    virtual void showFigureType()=0;
};
class Rectangle : public Figure
{
private:
    double width;
    double height;
public:
    Rectangle(double w, double h) : width(w), height(h)
    {
    }
    double getSquare() override
    {
        return width * height;
    }
    double getPerimeter() override
    {
        return width * 2 + height * 2;
    }
    void showFigureType()
    {
        std::cout << "Rectangle" << std::endl;
    }
};
class Circle : public Figure
{
private:
    double radius;
public:
    Circle(double r) : radius(r)
    {
    }
    double getSquare() override
    {
        return radius * radius * 3.14;
    }
    double getPerimeter() override
    {
        return 2 * 3.14 * radius;
    }
    void showFigureType()
    {
        std::cout << "Circle" << std::endl;
    }
};
 
int main()
{
    Rectangle rect(30, 50);
    Circle circle(30);
     
    std::cout << "Rectangle square: " << rect.getSquare() << std::endl;
    std::cout << "Circle square: " << circle.getSquare() << std::endl;
 
    return 0;
}
Здесь определены два класса-наследника от абстрактного класса Figure - Rectangle (прямоугольник) и Circle (круг). При создании классов-наследников все они должны либо определить для чстых виртуальных функций конкретную реализацию, либо повторить объявление чистой виртуальной функции. Во втором случае производные классы также будут абстрактными.

В данном же случае и Circle, и Rectangle являются конкретными классами и реализуют все виртуальные функции.

Консольный вывод программы:

Rectangle square: 1500
Circle square: 2826
Стоит отметить, что абстрактный класс может определять и обычные функции и переменные, может иметь несколько конструкторов, но при этом нельзя создавать объекты этого абстрактного класса.


Класс, объявленный внутри функции, становится локальным для этой функции и называется локальным классом в C ++.

Имя локального класса может использоваться только локально, т.Е. Внутри функции, а не вне ее.
Методы локального класса должны быть определены только внутри него.
Локальный класс может иметь статические функции, но не статические элементы данных.
Например, в следующей программе Test является локальным классом в fun().
// C++ program without any compilation error
// to demonstrate a Local Class
#include <iostream>
using namespace std;

// Creating the class
void fun()
{
	// local to fun
	class Test {
		// members of Test class
	};
}

// Driver Code
int main() { return 0; }

19. Требования к классам, объекты которых являются элементами последовательных контейнеров. Лямбда-функции в Си++. Использование лямбда-функций в алгоритмах. Сортировка контейнера алгоритмом sort с помощью предиката, лямбда-функции и перегрузки операции «<».

Правило трёх 
(также известное как «Закон Большой Тройки» или «Большая Тройка») - правило в C++, 
гласящее, что если класс или структура определяет один из следующих методов, 
то они должны явным образом определить все три метода: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 

С выходом одиннадцатого стандарта правило расширилось 
и теперь называется правило пяти. 
Теперь при реализации конструктора необходимо реализовать: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 
	• Конструктор перемещения 
	• Оператор присваивания перемещением

20. Функциональные объекты (функторы), перегрузка операции () (вызов функции) в классах, пример использования.

Правило трёх 
(также известное как «Закон Большой Тройки» или «Большая Тройка») - правило в C++, 
гласящее, что если класс или структура определяет один из следующих методов, 
то они должны явным образом определить все три метода: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 

С выходом одиннадцатого стандарта правило расширилось 
и теперь называется правило пяти. 
Теперь при реализации конструктора необходимо реализовать: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 
	• Конструктор перемещения 
	• Оператор присваивания перемещением

21. Требования к классам, объекты которых являются элементами контейнеров set и map.

22. Требования к классам, объекты которых являются элементами контейнеров unordered_set и unordered_map.

23. Понятие особой (исключительной) ситуации в Си++. Общий формат обработки исключительных ситуаций (ключевые слова: try, catch, throw).

Определение особой (исключительной) ситуации: 
	Любая ситуация, достижимая в процессе выполнения программы, 
	может быть объявлена программистом как особая или исключительная. 
	
	Например, какая-то переменная приняла значения в заданном диапазоне 
	(вышла из заданного диапазона), встретился конец файла, 
	деление на 0, переполнение и т.д.
try (контролировать);
try - блок в котором создается исключение 

catch (ловить);
сatch - ловит исключение 

throw (генерировать).
throw - генератор исключения 
Общий формат обработки исключительных ситуаций:
try // Контролируемый блок
{
……….
 if (условие_определяющее_искл_ситуацию)
 throw значение_исключения; // Генерация исключения
……….
}

// Один или несколько обработчиков исключений
catch(тип_исключения1 имя) { операторы }
………
catch(тип_исключенияN имя) { операторы }

Механизм работы: 
	Как только выполняется оператор генерации исключения: 
	throw значение_исключения;
	управление передается за пределы контролируемого блока 
	в один из подходящих обработчиков исключений catch. 
	
	Подходящий обработчик ищется по типу исключения. 
	
	Обработчики исключений похожи на функцию с одним параметром, 
	не возвращающую значение.
	
	После выполнения одного из обработчиков управление передается оператору, 
	который первым следует за обработчиками, 
	обратно в контролируемый блок управление не передается.
#include <iostream>

using namespace std; 

double mydiv(double x, double y)
{
	// Обработка исключения внутри функции 
	try // Контролируемый блок
	{
	if (y==0) throw '0'; // Генерация исключения 1  тип `char` 
	if (x<0) throw "negative 1 param"; // Генерация исключения 2  тип `char *` 
	if (y<0) throw "negative 2 param"; // Генерация исключения 3  тип `char *`
	return x/y;
	}
	catch(char * str) // Обработчик исключения 2 или 3 через параметр передается значение исключения
	{
		cout<<"nException: "<< str;
	}
	
	catch(char) // Обработчик исключения 1   для типа char
	{
		cout<<"nException: x="<<x<<" y="<<y;
	}
	
	return 0;
}

void main() {
	cout<<"nRez=" << mydiv(1, -2); 
}

image
image

Стандарт С11 содержит ключевое слово noexcept (аналог throw()). 
Означает, что функция не может генерировать не обработанное исключение: 
	void myfun() noexcept;

24. Обработка исключения внутри функции, в которой сгенерировано исключение (ключевые слова: try, catch, throw), пример.

См. вопрос 23

#include <iostream>

using namespace std; 

double mydiv(double x, double y)
{
	// Обработка исключения внутри функции 
	try // Контролируемый блок
	{
	if (y==0) throw '0'; // Генерация исключения 1  тип `char` 
	if (x<0) throw "negative 1 param"; // Генерация исключения 2  тип `char *` 
	if (y<0) throw "negative 2 param"; // Генерация исключения 3  тип `char *`
	return x/y;
	}
	catch(char * str) // Обработчик исключения 2 или 3 через параметр передается значение исключения
	{
		cout<<"nException: "<< str;
	}
	
	catch(char) // Обработчик исключения 1   для типа char
	{
		cout<<"nException: x="<<x<<" y="<<y;
	}
	
	return 0;
}

void main() {
	cout<<"nRez=" << mydiv(1, -2); 
}

25. Обработка исключения вне функции, в которой сгенерировано исключение, объявление функции, в которой генерируются исключения (ключевые слова: try, catch, throw), пример.

image

Стандарт С11 содержит ключевое слово noexcept (аналог throw()). 
Означает, что функция не может генерировать не обработанное исключение: 
	void myfun() noexcept;

26. Создание исключения как объекта класса в Си++ (ключевые слова: try, catch, throw), пример.

image

Основная идея: 
	Функция, сталкивающаяся с неразрешимой проблемой, 
	формирует исключение в надежде на то, 
	что вызывающая ее (прямо или косвенно) функция 
	сможет обработать исключение.

27. Формы обработчиков исключений (catch), примеры.

Формы обработчиков исключений 

После блока try может быть несколько обработчиков исключений, 
существует три различные формы: 

	catch(тип_искл имя_искл) {…..} 
	
	catch(тип_исключения) {…..} 
	/* не предусматривает использование значения исключения, важен тип */ 

	catch(…) {…..} 
	// Подходит для исключения любого типа 
	// обработчик должен быть последним
#include <iostream>
using namespace std;
class ZeroDivide {}; 
class Overflow {};
float mydiv(float x, float y)
{
	if (y==0) throw ZeroDivide(); 
	double z=x/y; 
	if (z>1e+30) throw Overflow();
	if (z==0) throw 1; // Исключение типа int 
			// (Сработает универсальный обработчик)
	return z;
}

void main() {
	try
	{
		cout<<"nRez="<< mydiv(1e20, 1e-20);
	}
	catch(Overflow) // Важен тип исключения имя параметра отсутствует
	{
		cout<<"nOverflow";
	}
	catch(ZeroDivide) {
		cout<<"nZeroDivide"; // Важен тип исключения имя параметра отсутствует
	}
	catch(...) // Обработчик без типа подходит для любого исключения должен быть последним
	{
		cout<<"nException";
	}
}
Сравнения по типам в обработчиках имеет более широкий смысл, 

например, 
	обработчик catch(T t) {…..} подходит для обработки исключений типа: 
		const T, T&, const T&, 
		и типов производных классов от T.

28. Формы выражений генерации исключений в Си++ (throw), примеры.

Формы выражений генерации исключений 2 формы выражений генерации исключений: 

	throw выражение; 
	/* 
	Исключение формируется как статический объект, 
	значение которого определяется выражением генерации 
	*/ 
	
	throw;
#include <iostream>
using namespace std; 
double mydiv(double x, double y)
{
	try
	{
		if (y==0) throw "ZeroDivide"; // Тип исключения `char *` 
		if (y<0) throw y; // Тип исключения double
		return x/y;
	}
	catch(char *str)
	{
		cout<<str;
	}
	catch(double y)
	{
		throw; // Ретрансляция исключения во внешний блок
	}
	return 0;
}

void main() {
	try  // Внешний блок try
	{
		cout<<"nRez="<< mydiv(9, -4);
	}
	catch(double a) // Сюда передается исключение из внутреннего блока
	{
		cout<<"nException: a="<< a;
	}
}
Оператор «ретранслирует» уже существующее исключение 
в блок try верхнего уровня, 
используется внутри блока catch в случае вложенных блоков try.

При вложении контролируемых блоков исключение, 
возникшее во внутреннем блоке, последовательно «просматривает» обработчики, 
переходя от внутреннего блока к внешнему, 
до тех пор, пока не будет найден подходящий обработчик. 

Если во всей совокупности обработчиков не будет найден подходящий, 
то выполняется аварийное завершение программы 

29. Обработка исключений в стандартной библиотеке Си++: обзор основных классов.

Все исключения являются производными от класса exception 
(пространство имен std, заголовочный файл exception). 

Класс содержит строку текста с описанием исключения (char *), 
есть конструктор для инициализации этой строки в производных классах от exception, 
в классе exception строка содержит «std::exception», 
чтобы получить указатель на строку, вызывается метод char * what();
Исключения можно разделить на 3 категории: 
	* языковая поддержка; 
	* логические ошибки; 
	* ошибки времени выполнения.

image

#include <string.h> 
#include <iostream> 
#include <exception>

using namespace std;

struct mydata: public exception 
// Класс для задания исключения при создании в полях хранится дополнительная информация об исключении
{
	double x, y; 
	mydata(double a, double b) : exception() {
		x=a; 
		y=b;
	}
};

double div(double x, double y)
{
	if (y == 0) throw exception();  // Исключение класса exception 
	if (y<0 || x<0) throw mydata(x, y); // Исключение класса производного от exception
	return x/y;
}

int main() {
	try
	{
		cout<<"Rez="<< div(5., -0.5)<<endl; 
	}
	catch(mydata & d) // Обработчик класса производного от exception
	{
		cout << endl << d.what()<< " x="<< d.x<<" y="<<d.y;
	}
	catch (exception & d) // Обработчик класса exception
	{
		cout << endl << d.what();
	}
	return 1;
}
Все функции в определении класса exception 
имеют пустую спецификацию noexcept (или ранее throw()).

Исключения языковой поддержки используются на уровне языка C++. 
Эти исключения генерируются при неудачных попытках выполнения некоторых операций.

Некоторые классы: 
	- исключение класса bad_alloc генерируется при 
		неудачном выполнении глобального оператора new 
		(кроме версии new с запретом исключений);
	
	- исключение класса bad_cast генерируется оператором dynamic_cast, 
		если преобразование типа по ссылке во время выполнения завершается неудачей; 
	
	- исключение класса bad_typeid генерируется оператором typeid, 
		предназначенным для идентификации типов по время выполнения, 
		если аргументом typeid является ноль или null-указатель, 
		генерируется исключение; 
	
	- исключение класса bad_exception предназначено для обработки непредвиденных исключений.
Логические ошибки обусловлены нарушением внутренней логики программы.
Предполагается, что их можно найти и предотвратить еще до начала выполнения программы.

Базовый класс для логических ошибок:

class logic_error : public exception

В стандартной библиотеке определены следующие классы для логических ошибок 
(заголовки классов):

	class invalid_argument : public logic_error  // неверный аргумент 
	
	class out_of_range : public logic_error  // вне диапазона 
	
	class length_error : public logic_error  // неверная длина 
	
	class domain_error : public logic_error  // вне допустимой области
	
	
Ошибки времени выполнения связаны с событием,  с самой программой не связанным.
Предполагается, что их нельзя обнаружить, пока программа не начала работать.
Базовый класс:
	class runtime_error : public exception  // ошибка времени выполнения

Классы ошибок времени выполнения:
	class range_error : public runtime_error  // ошибка диапазона

Функция может возбудить исключение range_error, чтобы сообщить об ошибке во внутренних вычислениях.
	class overflow_error : public runtime_error  // переполнение 

	class underflow_error : public runtime_error { // потеря значимости

30. Обработка исключений в стандартной библиотеке Си++: создание своего класса исключения на основе класса std::exception, пример.

31. Многозадачность в стандартной библиотеке C++: высокоуровневый интерфейс (функция std::async() и шаблон класса std::future< >), пример создания потока и получение результата потоковой функции.

32. Многозадачность в стандартной библиотеке C++: низкоуровневый интерфейс (использование класса std::thread), пример.

33. Синхронизация в стандартной библиотеке C++. Использование класса std::mutex (взаимное исключение), пример.

int main()
{
    unsigned long long g_count = 0;
    std::mutex g_count_mutex;

    std::thread t1([&]()
    {
        for(auto i = 0; i < 1'000'000; ++i) {
            g_count_mutex.lock();
            g_count += 1;
            g_count_mutex.unlock();
        }
    });
    
    std::thread t2([&]()
    {
        for(auto i = 0; i < 1'000'000; ++i) {
            g_count_mutex.lock();
            g_count += 1;
            g_count_mutex.unlock();
        }
    });
    
    t1.join();
    t2.join();
    
    std::cout << g_count;

    return 0;
}

В этом примере поток перед тем как изменить переменную захватывает mutex (устанавливает флаг о том, что переменная занята), а другой поток, пытаясь захватить тот же mutex в это же время, обнаруживает, что первый поток уже работает с переменной, и дожидается её освобождения.

Подробнее про mutex:

std::mutex
Используя mutex в примере выше, мы синхронизируем работу потоков. Mutex является примитивом синхронизации.

Примитивы синхронизации – механизмы, позволяющие реализовать взаимодействие потоков, например, единовременный доступ только одного потока к критической области.

Примитивы синхронизации преследуют различные задачи:

Взаимное исключение потоков – примитивы синхронизации гарантируют то, что единовременно с критической областью будет работать только один поток.

Синхронизация потоков – примитивы синхронизации помогают отслеживать наступление тех или иных конкретных событий, то есть поток не будет работать, пока не наступило какое-то событие. Другой поток в таком случае должен гарантировать наступление данного события.

34. Использование для синхронизации потоков блокировок – шаблона std::lock_guard, пример.

35. Использование условных переменных (объекты класса std::condition_variable) для синхронизации потоков в стандартной библиотеке C++: посылка и принятие оповещений потоками, примеры.

36. Использование атомарных операций (шаблон std::atomic) для синхронизации потоков в стандартной библиотеке C++.

37. Умный указатель (smart pointer) std::unique_ptr, внутреннее устройство, пример использования.

Правило трёх 
(также известное как «Закон Большой Тройки» или «Большая Тройка») - правило в C++, 
гласящее, что если класс или структура определяет один из следующих методов, 
то они должны явным образом определить все три метода: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 

С выходом одиннадцатого стандарта правило расширилось 
и теперь называется правило пяти. 
Теперь при реализации конструктора необходимо реализовать: 
	• Деструктор 
	• Конструктор копирования 
	• Оператор присваивания копированием 
	• Конструктор перемещения 
	• Оператор присваивания перемещением
Умные указатели (smart pointers) набор классов (шаблонов классов) библиотеки С++.

Назначение – автоматизировать работу с динамической памятью, 
основная цель предотвращение утечки памяти.

Объекта класса типа умного указателя поддерживает основные операции
применяемые к обычным (иногда используется термин «сырой» указатель) указателям,
такие как, *, -> за счет перегрузки этих операций.

При в умных указателях применяется принцип Resource Acquisition Is Initialization
(RAII) – «Получение ресурса есть инициализация», смысл которого заключается в том, 
что с помощью тех или иных программных механизмов получение некоторого ресурса
неразрывно совмещается с инициализацией, а освобождение — с уничтожением объекта. 

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

Для использования умных указателей подключается заголовочный файл <memory>
**std::unique_ptr простейший умный указатель, является шаблоном класса
(перегруженным шаблоном, существует два шаблона: 
	один для отдельного объекта, 
	второй – для массива объектов)**. 

Указатель unique_ptr всегда полностью владеет объектом, т.е. на
один объект указывает только один указатель, 
на один объект не могут указывать два или более указателей. 
Поэтому в шаблоне класса конструктор 
**копирования и оператор присваивания с копированием удалены**, 
**конструктор перемещения и оператор присваивания с перемещением присутствуют**
Для создания указателя std::unique_ptr рекомендуется использовать глобальную
шаблонную функцию std::make_unique, которая получает параметры как у конструктора
объекта, на который указывает указатель.

int main()
{
	unique_ptr<A> pA=make_unique<A>(1, 2);
	cout<<*pA<<endl;
	return 0;
}
//Мой пример ответа на вопрос:
/*
 * Умные указатели - набор классов шаблонов предназначенных для автоматизации работы с динамической памятью.
 * Используется прицип RAII - получение ресурса есть инициализация.
 *
 *
 * std::unique_ptr
 * - Основная фича : на один объект может указывать тоьлко один указатаель. 
 *   Соответственоо, у него отсутсвуют конструктор и оператор присваивания копированием. 
 *   Но должны быть перемещения
 *   std::unique_ptr<type> pA(new obj())
 *
 *   рекомендуется использовать make_unique
 *
 *   std::unique_ptr<type> pA = std::make_unique<type>(args);
 *
 *
 */


#include <iostream>
#include <memory>


struct A {
	int a;
	int *p = nullptr;

	A(int a) : a(a) {
		p = new int[100];
	};
	A() {a=0;};
	~A() {
	delete p;
	std::cout << "~A()" << std::endl;
	};




};

std::ostream &operator<<(std::ostream & out, A &obj){

	out << "a = " << obj.a<< std::endl;
	return out;
}



int main() {

std::unique_ptr pA = std::make_unique<A>(5);

std::cout << *pA << std::endl;
pA->a = 505;
std::cout << *pA << std::endl;

//std::cout << pA->a << std::endl;
}

38. Умный указатель (smart pointer) std::shared_ptr, внутреннее устройство, пример использования.

В отличии от unique_ptr разрешает копирование, 
таким образом, на один объект могут указывать два или более указателей. 

По аналогии с unique_ptr рекомендуется 
объект с указателем shared_ptr создавать с помощью глобальной функции std::make_shared 
(также передаются параметры конструктора объекта класса).

Пример создания объекта:

int main()
4 // ?
{
	shared_ptr<A> pA=make_shared<A>(1, 2);
	cout<<*pA<<endl;
	return 0;
}

shared_ptr должен каким-то образом знать, а существуют ли другие shared_ptr,
которые указывают на тот же самый объект. 

Когда для shared_ptr вызывается деструктор,
то он должен определить, удалять объект, на который он указывает, или нет. 

Если существуют другие shared_ptr, указывающие на этот же объект, 
то объект не удаляется, если других shared_ptr, указывающих на объект не существует, 
то объект удаляется. 

Для этого в динамической памяти (куче) создается небольшой объект ControlBlock. 
В этом контрольном блоке сохраняется счетчик ссылок, 
т.е. текущее количество shared_ptr указывающих на текущий объект. 

У себя shared_ptr хранит указатель на этот контрольный блок. 
Таким образом shared_ptr имеет два указателя: на сам объект и на контрольный блок.
При создании копии указателя копируются два указателя 
и в контролируем блоке счетчик объектов увеличивается на 1. 
В деструкторе shared_ptr в контролируемом блоке счетчик объектов уменьшается на 1, 
если он стал равен 0, это значит - удаляется последний shared_ptr, 
указывающий на объект и объект уничтожается, 
также освобождается память, выделенная для ControlBlock.

При перемещении shared_ptr счетчик в ControlBlock не изменяется. 
Но в случае перегрузки оператора присваивания с копированием или перемещением. 
Если объект, в который копируем или перемещаем, отличается от копируемого или перемещаемого 
и не является пустым, то для этого объекта уменьшаем счетчик ссылок на 1, 
если счетчик равен 0, то объект удаляем, и удаляем счетчик ссылок для него.
Значение счетчика можно получить с помощью функции класса:
long use_count() const noexcept
//Пример моего решения 
/*
 * shared_ptr отличается от unique тем, что создает объект ControlBlock с 
 * счётчиком кол-ва указателей  на данный объект.
 * При освобождении указателся, проверяется, существуют ли другие shared_ptr
 * если да, то деструторк объекта не вызвавется
 * если нет, то вызывается и удаляется ообъект
 *
 *
 * Значение счетчика long use_count() const noexcpet
 */







#include <iostream>
#include <memory>

struct A {
	int a;
	int *p = nullptr;

	A(int a) : a(a) {
		p = new int[100];
	};
	A() {a=0;};
	~A() {
	delete p;
	std::cout << "~A()" << std::endl;
	};




};

std::ostream &operator<<(std::ostream & out, A &obj){

	out << "a = " << obj.a<< std::endl;
	return out;
}



int main() {

	std::shared_ptr<A> pA1 = std::make_shared<A>(2);
	std::shared_ptr<A> pA2 = std::make_shared<A>(5);
	std::cout << "Counters : "<<pA1.use_count() << " " << pA2.use_count() << std::endl;
	std::cout << "Class fields : "<<pA1->a << " " << pA2->a << std::endl;
	std::shared_ptr<A> pA1cp = pA1;
	std::shared_ptr<A> pA2cp = pA2;
	std::cout << "Counters : "<<pA1.use_count() << " " << pA2.use_count() << std::endl;
	std::cout << "Class fields : "<<pA1->a << " " << pA2->a << std::endl;

}

39. Умный указатель (smart pointer) std::weak_ptr, назначение, пример использования.

Не владеющий (слабый) указатель. 
Можно проинициализировать указателем shared_ptr при этом значение счетчика ссылок не увеличится. 
С помощью метода weak_ptr::lock() получаем shared_ptr для объекта, 
если объект существует (существует хотя бы один shared_ptr для объекта). 
Если объект уже не существует (перестали существовать все shared_ptr на него), 
то lock() вернет «пустой» shared_ptr.
int main()
{
	weak_ptr<A> pA_W;
	{
		shared_ptr<A> pA=make_shared<A>(1, 2);
		pA_W=pA; // Инициализация weak_ptr
		cout<<pA_W.use_count()<<endl; // 1
		auto p=pA_W.lock(); // p имеет тип shared_ptr<A>

		if (p)
		cout<<*p<<endl; // Печатается объект

		else cout<<"Object deleted!!!"<<endl;
		cout<<pA_W.use_count()<<endl; // 2
	} // При выходе из блока все shared_ptr<A> уничтожаются

	auto p=pA_W.lock(); // Вернет пустой shared_ptr<A>

	if (p)
	cout<<*p<<endl;

	else cout<<"Object deleted!!!"<<endl; // Печатается Object deleted!!!
	cout<<pA_W.use_count()<<endl; // 0

	return 0;
}

40. Перегрузка операторов new и delete.

41. Размещающий оператор new и его перегрузка.

42. Передача дополнительных параметров в оператор new при его перегрузке.

43. Дополнительные операции преобразования типов: const_cast, dynamic_cast, static_cast, reinterpret_cast.

44. Понятие о динамической идентификации типов в Си++. Операция typeid. Основные функции класса typeinfo. Примеры использования.

Обновлено: 20.05.2023

В объектно-ориентированном программировании конструктор класса (от англ. constructor , иногда сокращают ctor) — специальный блок инструкций, вызываемый при создании объекта, причём или при его объявлении (располагаясь в стеке или в статической памяти, что допустимо в C++, но не в куче при использовании ключевого слова new .

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

Термин конструктор также используется для обозначения одного из тегов, описывающих данные в алгебраическом типе данных. Это использование несколько отличается от описываемого в статье. Для дополнительной информации смотрите Алгебраический тип данных.

В большинстве языков конструктор может быть перегружен, что позволяет использовать несколько конструкторов в одном классе, причём каждый конструктор может иметь различные параметры. Некоторые языки различают несколько особых типов конструкторов:

    — конструктор, не принимающий аргументов. — конструктор, принимающий в качестве аргумента объект того же класса (или ссылку из него).

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

Содержание

Виды конструкторов

Конструктор по умолчанию

Конструктор не имеющий обязательных аргументов. Используется при создании массивов объектов, вызываясь для создания каждого экземпляра. В отсутствие явно заданного конструктора по умолчанию его код генерируется компилятором (что на исходном тексте, естественно, не отражается).

Конструктор копирования

Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функции по значению.

Конструктор копирования нужен, например, если для хранения данных объекта требуется дополнительно выделяемая память. Если его не будет, то конструктором копирования (сгенерированным компилятором) будут скопированы указатели, адресующие данные прежнего объекта (без выделения новой памяти). Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе.

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

Конструктор преобразования

Конструктор, принимающий один агрумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально.

Виртуальный конструктор

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

  • Этот класс является потомком некоего наперёд заданного класса (в данном примере это класс TVehicle )
  • На всём пути наследования от базового класса к создаваемому цепочка переопределения не обрывалась (при переопределении виртуального метода синтаксис Delphi требует ключевое слово override для переопределения функции либо reintroduce для задания новой функции с тем же именем).

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

Такой механизм позволяет создавать объекты любого заранее неизвестного класса, производного от TVehicle .

Заметьте, что код

является некорректным — директива reintroduce разорвала цепочку переопределения виртуального метода, и в действительности будет вызван конструктор TMotorcycle.Create (а значит, будет создан мотоцикл, а не мопед!)

Синтаксис

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

Python

В языке Python конструктором является метод класса с именем __init__ . Кроме того не следует забывать, что первым аргументом любого метода должен быть указатель на контекст класса self.

Пример

Delphi

В C++, для объявления конструктора служит ключевое слово constructor . Имя конструктора может быть любым, но рекомендуется называть конструктор Create .

Пример

  • Конструкторы не имеют чётко определённого типа возвращаемых данных.
  • Конструкторы не могут напрямую вызываться (необходимо использовать ключевое слово new ).
  • Конструкторы не могут быть synchronized, final, abstract, native и static типов.
  • Конструкторы всегда выполняются в том же потоке.

Пример

Конструкторы в Visual Basic используют обычный метод объявления с именем New .

Пример

Пример

Eiffel

В Эйфеле подпрограммы, которые инициализируют объекты, называются процедурами создания. Процедуры создания в чём-то подобны конструкторам и в чём-то отличаются. Они имеют следующие характеристики:

  • Процедуры создания не имеют никакого явного типа результата возврата (по определению процедуры[Примечание 1] ).
  • Процедуры создания поименованы. Имена ограничены допустимыми идентификаторами.
  • Процедуры создания задаются по именам в тексте класса.
  • Процедуры создания могут быть вызваны напрямую (как обычные процедуры) для повторной инициализации объектов.
  • Каждый эффективный (т.е. конкретный, не абстрактный) класс должен (явно или неявно) указать по крайней мере одну процедуру создания.
  • Процедуры создания отвечают за приведение только что проинициализированного объекта в состояние, которое удовлетворяет инварианту класса [Примечание 2] .

Хотя создание объекта является предметом некоторых тонкостей [Примечание 3] , создание аттрибута с типовым объявлением x: T , выраженном в виде инструкции создания create x.make состоит из следующей последовательности шагов:

  • Создать новый непосредственный экземпляр типа T [Примечание 4] .
  • Выполнить процедуру создания make для вновь созданного экземпляра.
  • Прикрепить вновь созданный объект к сущности x .

Пример

В первом отрывке ниже определяется класс POINT . Процедура make кодируется после ключевого слова feature .

Ключевое слово create вводит список процедур, которые могут быть использованы для инициализации экземпляров класса. В данном случае список содержит default_create , процедуру с пустой реализацией, унаследованной из класса ANY , и процедуру make с реализацией в самом классе POINT .

Во втором отрывке класс, являющийся клиентом класса POINT , имеет объявления my_point_1 и my_point_2 типа POINT .

В коде подпрограммы my_point_1 создаётся с координатами (0.0; 0.0). Поскольку в инструкции создания не указана процедура создания, используется процедура default_create , унаследованная из класса ANY . Эта же строка могла бы быть переписана как create my_point_1.default_create . Только процедуры, указанные как процедуры создания могут использоваться в инструкциях создания (т.е. в инструкциях с ключевым словом create ).

Следующей идёт инструкция создания для my_point_2 , задающая начальные значения для координат my_point_2 .

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

Пример

Необходимо отметить, что в ColdFusion не существует метода-конструктора. Широкое распространение среди сообщества программистов на ColdFusion получил способ вызова метода ‘ init ‘, выступающего в качестве псевдоконструктора.

Пример

В PHP (начиная с версии 5) конструктор — это метод __construct() , который автоматически вызывается ключевым словом new после создания объекта. Обычно используется для выполнения различных автоматических инициализаций, как например, инициализация свойств. Конструкторы также могут принимать аргументы, в этом случае, когда указано выражение new , необходимо передать конструктору формальные параметры в круглых скобках.

Тем не менее, конструктор в PHP версии 4 (и ранее) — метод класса с именем этого же класса.

Упрощенные конструкторы (с псевдокодом)

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

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

Примечания

  1. ↑Подпрограммы Эйфеля являются либо процедурами либо функциями. У процедур нет никакого возвращаемого типа. Функции всегда имеют возвращаемый тип.
  2. ↑ Поскольку должен быть также удовлетворён инвариант наследуемого(-ых) класса(-ов), нет обязательного требования вызова родительских конструкторов.
  3. ↑ Полная спецификация содержится в стандартах ISO/ECMA по языку программироная Эйфель в он-лайн доступе. [1]
  4. ↑ Стандарт Эйфеля требует, чтобы поля были инициализированы при первом доступе к ним, т.ч. нет необходимости осуществлять их инициализацию значениями по умолчанию во время создания объекта.

Ссылки

См. также

Wikimedia Foundation . 2010 .

Полезное

Смотреть что такое «Конструктор класса» в других словарях:

конструктор класса — Специальный блок инструкций, вызываемый при создании объекта. [ГОСТ Р 54456 2011] Тематики телевидение, радиовещание, видео EN class constructor … Справочник технического переводчика

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

Конструктор объекта — В объектно ориентированном программировании конструктор класса (от англ. constructor, иногда сокращают ctor) специальный блок инструкций, вызываемый при создании объекта, причём или при его объявлении (располагаясь в стеке или в статической… … Википедия

Конструктор по умолчанию — (англ. default constructor), в объектно ориентированных языках программирования конструктор, который может быть вызван без аргументов. В C++ и Java если нет явным образом опредёленных конструкторов в классе, то компилятор использует… … Википедия

конструктор по умолчанию — Конструктор, создаваемый компилятором при отсутствии конструктора класса. [ГОСТ Р 54456 2011] Тематики телевидение, радиовещание, видео EN default constructor … Справочник технического переводчика

Конструктор копирования — Конструктором копирования (в англоязычной литературе используется термин copy constructor) называется специальный конструктор в языке программирования C++, применяемый для создания нового объекта как копии уже существующего. Такой конструктор… … Википедия

Поле класса — или атрибут (переменная член, data member, class field, instance variable) в объектно ориентированном программировании переменная, связанная с классом или объектом. Все данные объекта хранятся в его полях. Доступ к полям осуществляется по… … Википедия

АПЛ Класса «Борей» — РПКСН проекта 955 «Борей» схема проекта Основные характеристики Тип корабля РПКСН … Википедия

Деструктор класса — Деструктор специальный метод класса, служащий для деинициализации объекта (например освобождения памяти). Содержание 1 Деструктор в Delphi 2 Деструктор в С++ 3 Виртуальный деструктор … Википедия

Подводные лодки класса «Декабрист» — Многоцелевая ПЛ «Декабрист» серия I музей Д 2 «Народоволец» Основные характеристики Тип корабля Большая торпедная подводная лодка … Википедия

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

В следующем примере класс с именем Taxi определяется с помощью простого конструктора. Затем оператор new создает экземпляр этого класса. Конструктор Taxi вызывается оператором new сразу после того, как новому объекту будет выделена память.

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

Создание экземпляров класса можно запретить, сделав конструктор закрытым, следующим образом:

Дополнительные сведения см. в разделе Закрытые конструкторы.

Конструкторы для типов структур похожи на конструкторы классов, но structs не могут содержать явный конструктор без параметров, так как он предоставляется компилятором автоматически. Этот конструктор инициализирует каждое поле в struct со значением по умолчанию. При этом конструктор без параметров вызывается только в том случае, если экземпляр struct создается с помощью переменной new . Например, этот код использует конструктор без параметров, Int32чтобы гарантировать, что целое число инициализируется:

Однако следующий код вызывает ошибку компилятора, так как он не используется new , и потому что он пытается использовать объект, который не был инициализирован:

Кроме того, объекты на основе structs (включая все встроенные числовые типы) можно инициализировать или назначить, а затем использовать, как в следующем примере:

Поэтому вызов конструктора без параметров для типа значения не требуется.

Оба класса и structs могут определять конструкторы, принимающие параметры. Конструкторы, принимающие параметры, необходимо вызывать с помощью оператора new или base. Классы и structs могут определять также несколько конструкторов; для определения конструктора без параметров ни один их них не требуется. Пример:

Этот класс можно создать, воспользовавшись одним из следующих операторов:

Конструктор может использовать ключевое слово base для вызова конструктора базового класса. Пример:

В этом примере конструктор базового класса вызывается перед выполнением соответствующего ему блока. Ключевое слово base можно использовать как с параметрами, так и без них. Любые параметры для конструктора можно использовать как параметры для base или как часть выражения. Дополнительные сведения см. в разделе base.

В производном классе, если конструктор базового класса не вызывается явным образом с помощью base ключевого слова, конструктор без параметров, если он есть, вызывается неявно. Это означает, что следующие объявления конструкторов действуют одинаково:

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

Конструктор может вызывать другой конструктор в том же объекте с помощью ключевого слова this. Как и base , this можно использовать с параметрами или без, а все параметры в конструкторе доступны как параметры this или как часть выражения. Например, второй конструктор в предыдущем примере можно переписать, используя this :

Применение ключевого слова this в приведенном выше примере привело к вызову конструктора:

Конструкторы могут иметь пометку public, private, protected, internal, protected internal или private protected. Эти модификаторы доступа определяют, каким образом пользователи класса смогут создавать класс. Дополнительные сведения см. в статье Модификаторы доступа.

Конструктор можно объявить статическим, используя ключевое слово static. Статические конструкторы вызываются автоматически непосредственно перед доступом к статическим полям и обычно используются для инициализации членов статического класса. Дополнительные сведения см. в разделе Статические конструкторы.

Чтобы настроить, как класс инициализирует его члены или вызывать функции при создании объекта класса, определите конструктор. Конструкторы имеют имена, совпадающие с именами классов, и не имеют возвращаемых значений. Вы можете определить столько перегруженных конструкторов, сколько необходимо для настройки инициализации различными способами. Как правило, конструкторы имеют открытые специальные возможности, чтобы код за пределами определения класса или иерархии наследования может создавать объекты класса. Но вы также можете объявить конструктор как protected или private .

Конструкторы могут при необходимости принимать список инициализаторов элементов. Это более эффективный способ инициализации членов класса, чем назначение значений в тексте конструктора. В следующем примере показан класс Box с тремя перегруженными конструкторами. Последние два используют списки инициализации элементов:

При объявлении экземпляра класса компилятор выбирает, какой конструктор будет вызываться на основе правил разрешения перегрузки:

  • Конструкторы могут быть объявлены как inline , , explicitfriend или constexpr .
  • Конструктор может инициализировать объект, объявленный как const , volatile или const volatile . Объект становится const после завершения конструктора.
  • Чтобы определить конструктор в файле реализации, присвойте ему полное имя, как и любая другая функция-член: Box::Box() .

Списки инициализаторов элементов

При необходимости конструктор может иметь список инициализаторов элементов, который инициализирует члены класса перед запуском тела конструктора. (Список инициализаторов элементов не совпадает со списком инициализаторов типа std::initializer_list .)

Предпочитать инициализаторы элементов перечисляют значения вместо назначения значений в тексте конструктора. Список инициализаторов элементов напрямую инициализирует элементы. В следующем примере показан список инициализаторов элементов, состоящий из всех identifier(argument) выражений после двоеточия:

Идентификатор должен ссылаться на член класса; он инициализирован со значением аргумента. Аргумент может быть одним из параметров конструктора, вызова функции или . std::initializer_list

const члены и члены ссылочного типа должны быть инициализированы в списке инициализаторов элементов.

Чтобы обеспечить полную инициализацию базовых классов перед запуском производного конструктора, вызовите все параметризованные конструкторы базового класса в списке инициализаторов.

Конструкторы по умолчанию

Конструкторы по умолчанию обычно не имеют параметров, но они могут иметь параметры со значениями по умолчанию.

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

Если используется неявный конструктор по умолчанию, обязательно инициализировать элементы в определении класса, как показано в предыдущем примере. Без этих инициализаторов члены будут неинициализированы, а вызов Volume() создаст значение мусора. Как правило, рекомендуется инициализировать элементы таким образом, даже если не используется неявный конструктор по умолчанию.

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

Конструктор по умолчанию, созданный компилятором, будет определен как удаленный, если какие-либо члены класса не являются конструктором по умолчанию. Например, все члены типа класса и их члены класса должны иметь конструктор по умолчанию и деструкторы, которые доступны. Все члены данных ссылочного типа и все const члены должны иметь инициализатор элементов по умолчанию.

При вызове конструктора по умолчанию, созданного компилятором, и пытаетесь использовать круглые скобки, выдается предупреждение:

Это утверждение является примером проблемы «Большинство vexing Parse». Можно интерпретировать myclass md(); как объявление функции или как вызов конструктора по умолчанию. Поскольку средства синтаксического анализа C++ предпочитают объявления по сравнению с другими вещами, выражение рассматривается как объявление функции. Дополнительные сведения см. в разделе «Большинство синтаксического анализа».

Если объявлены какие-либо конструкторы, отличные от по умолчанию, компилятор не предоставляет конструктор по умолчанию:

Если у класса нет конструктора по умолчанию, массив объектов этого класса нельзя создать с помощью синтаксиса квадратной скобки. Например, учитывая предыдущий блок кода, массив Boxes нельзя объявить следующим образом:

Однако для инициализации массива объектов Box можно использовать набор списков инициализаторов:

Дополнительные сведения см. в разделе «Инициализаторы».

Конструкторы копии

Конструктор копирования инициализирует объект, копируя значения элементов из объекта того же типа. Если члены класса являются простыми типами, такими как скалярные значения, конструктор копирования, созданный компилятором, достаточно, и вам не нужно определять собственные. Если для класса требуется более сложная инициализация, необходимо реализовать пользовательский конструктор копирования. Например, если член класса является указателем, необходимо определить конструктор копирования для выделения новой памяти и копирования значений из объекта, на который указывает другой объект. Конструктор копирования, созданный компилятором, просто копирует указатель, чтобы новый указатель по-прежнему указывал на расположение памяти другого пользователя.

Конструктор копирования может иметь одну из следующих сигнатур:

При определении конструктора копирования необходимо также определить оператор присваивания копирования (=). Дополнительные сведения см. в разделе «Назначение » и » Копирование конструкторов» и операторов присваивания копирования.

Вы можете запретить копирование объекта, определив конструктор копирования как удаленный:

При попытке копирования объекта возникает ошибка C2280: попытка ссылаться на удаленную функцию.

Конструкторы перемещения

Конструктор перемещения — это специальная функция-член, которая перемещает владение данными существующего объекта в новую переменную без копирования исходных данных. Он принимает ссылку rvalue в качестве первого параметра, а все последующие параметры должны иметь значения по умолчанию. Конструкторы перемещения могут значительно повысить эффективность программы при передаче больших объектов.

Компилятор выбирает конструктор перемещения, когда объект инициализируется другим объектом того же типа, если другой объект будет уничтожен и больше не нуждается в его ресурсах. В следующем примере показано одно дело, когда конструктор перемещения выбирается с помощью разрешения перегрузки. В конструкторе, который вызывает get_Box() , возвращаемое значение является xvalue (значение eXpiring). Поэтому он не назначается какой-либо переменной и поэтому выходит за пределы области действия. Чтобы обеспечить мотивацию для этого примера, давайте предоставим Box большой вектор строк, представляющих его содержимое. Вместо копирования вектора и его строк конструктор перемещения «крадет» его из значения «box», чтобы вектор теперь принадлежит новому объекту. Вызов std::move необходим, так как оба vector класса string реализуют собственные конструкторы перемещения.

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

Неявно объявленный конструктор перемещения определяется как удаленный, если какие-либо элементы, являющиеся типами классов, не имеют деструктора или если компилятор не может определить, какой конструктор следует использовать для операции перемещения.

Дополнительные сведения о написании конструктора нетривиального перемещения см. в разделе «Конструкторы перемещения» и «Операторы присваивания перемещения» (C++).

Явно заданные по умолчанию и удаленные конструкторы

Конструкторы копирования по умолчанию , конструкторы по умолчанию, конструкторы перемещения, операторы присваивания копирования, операторы присваивания перемещения и деструкторы. Вы можете явно удалить все специальные функции-члены.

Конструкторы constexpr

Конструктор может быть объявлен как constexpr , если

  • он либо объявлен как стандартный, либо удовлетворяет всем условиям для функций constexpr в целом;
  • класс не имеет виртуальных базовых классов;
  • каждый из параметров является литеральным типом;
  • тело не является блоком try-block функции;
  • инициализированы все нестатические члены данных и подобъекты базового класса;
  • Значение , если класс является (a) объединением, имеющим члены варианта, или (б) имеет анонимные объединения, инициализируется только один из членов профсоюза;
  • каждый нестатический член данных типа класса, а все подобъекты базового класса имеют конструктор constexpr.

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

Затем создайте объекты Box следующим образом:

Явные конструкторы

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

Можно инициализировать Box следующим образом:

Или передать целое значение функции, принимающей объект Box:

В некоторых случаях подобные преобразования могут быть полезны, однако чаще всего они могут привести к незаметным, но серьезным ошибкам в вашем коде. Как правило, необходимо использовать ключевое explicit слово в конструкторе (и определяемых пользователем операторах), чтобы предотвратить такое неявное преобразование типов:

Когда конструктор является явным, эта строка вызывает ошибку компилятора: ShippingOrder so(42, 10.8); . Дополнительные сведения см. в разделе о преобразованиях определяемых пользователем типов.

Порядок строительства

Конструктор выполняет свою работу в следующем порядке.

Вызывает конструкторы базовых классов и членов в порядке объявления.

Если класс является производным от виртуальных базовых классов, конструктор инициализирует указатели виртуальных базовых классов объекта.

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

Выполняет весь код в теле функции.

В следующем примере показан порядок, в котором конструкторы базовых классов и членов вызываются в конструкторе для производного класса. Сначала вызывается базовый конструктор. Затем члены базового класса инициализируются в том порядке, в котором они отображаются в объявлении класса. Наконец, вызывается производный конструктор.

Выходные данные будут выглядеть следующим образом.

Конструктор производного класса всегда вызывает конструктор базового класса, чтобы перед выполнением любых дополнительных операций иметь в своем распоряжении полностью созданные базовые классы. Конструкторы базового класса вызываются в порядке наследования, например, если ClassA является производным от , производным от ClassC ClassB которого является конструктор, ClassC сначала вызывается конструктор, а затем ClassB конструктор, а затем ClassA конструктор.

Если базовый класс не имеет конструктора по умолчанию, необходимо указать параметры конструктора базового класса в конструкторе производного класса:

Если конструктор создает исключение, то удаление выполняется в порядке, обратном созданию.

Отменяется код в теле функции конструктора.

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

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

Производные конструкторы и расширенная инициализация агрегатов

Если конструктор базового класса не является открытым, но доступен для производного класса, нельзя использовать пустые фигурные скобки для инициализации объекта производного типа в /std:c++17 режиме, а затем в Visual Studio 2017 и более поздних версий.

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

В C++17 Derived теперь считается агрегатным типом. Это означает, что инициализация Base через закрытый конструктор по умолчанию происходит непосредственно как часть расширенного правила агрегатной инициализации. Ранее частный Base конструктор был вызван через Derived конструктор, и он был успешно выполнен из-за friend объявления.

В следующем примере показано поведение C++17 в Visual Studio 2017 и более поздних версий в /std:c++17 режиме:

Конструкторы для классов с множественным наследованием

Если класс является производным от нескольких базовых классов, конструкторы базового класса вызываются в порядке, в котором они перечислены в объявлении производного класса:

Должны выводиться следующие выходные данные:

Делегирующие конструкторы

Делегирующий конструктор вызывает другой конструктор в том же классе для выполнения некоторых действий по инициализации. Эта функция полезна, если у вас есть несколько конструкторов, которые все должны выполнять аналогичную работу. Основную логику можно написать в одном конструкторе и вызвать из других. В следующем тривиальном примере Box(int) делегирует свою работу Box(int,int,int):

Объект, созданный конструкторами, полностью инициализируется сразу после выполнения любого конструктора. Дополнительные сведения см. в разделе «Делегирование конструкторов».

Наследование конструкторов (C++11)

Производный класс может наследовать конструкторы от прямого базового класса с помощью using объявления, как показано в следующем примере:

Visual Studio 2017 и более поздних версий: оператор using в /std:c++17 режиме и более поздних версиях преобразует все конструкторы из базового класса, за исключением тех, которые имеют идентичную сигнатуру конструкторам в производном классе. Как правило, рекомендуется использовать наследуемые конструкторы, когда производный класс не объявляет новые члены данных или конструкторы.

Шаблон класса может наследовать все конструкторы от аргумента типа, если этот тип определяет базовый класс:

Производный класс не может наследоваться от нескольких базовых классов, если эти базовые классы имеют конструкторы с одинаковой сигнатурой.

Конструкторы и составные классы

Классы, содержащие члены типа класса, называются составными классами. При создании члена типа класса составного класса конструктор вызывается перед собственным конструктором класса. Если у содержащегося класса нет конструктора по умолчанию, необходимо использовать список инициализации в конструкторе составного класса. В предыдущем примере StorageBox при присвоении типу переменной-члена m_label нового класса Label необходимо вызвать конструктор базового класса и инициализировать переменную m_label в конструкторе StorageBox :

В объектно-ориентированном программировании конструктор класса (от англ. constructor , иногда сокращают ctor) — специальный блок инструкций, вызываемый при создании объекта.

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

Термин «конструктор» также используется для обозначения одного из тегов, описывающих данные в алгебраическом типе данных. Это использование несколько отличается от описываемого в статье. Для дополнительной информации смотрите Алгебраический тип данных.

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

Содержание

Назначение конструктора

Одна из ключевых особенностей ООП — инкапсуляция: внутренние поля объекта напрямую недоступны, и пользователь может работать с объектом только как с единым целым, через открытые ( public ) методы. Каждый метод, в идеале, должен быть устроен так, чтобы объект, находящийся в «допустимом» состоянии (то есть когда выполняется инвариант класса), после вызова метода также оказался в допустимом состоянии. И первая задача конструктора — перевести поля объекта в такое состояние.

Вторая задача — упростить пользование объектом. Объект — не «вещь в себе», ему часто приходится требовать какую-то информацию от других объектов: например, объект File , создаваясь, должен получить имя файла. Это можно сделать и через метод:

Но удобнее открытие файла сделать в конструкторе: [1]

Виды конструкторов

Некоторые языки программирования различают несколько особых типов конструкторов:

    — конструктор, не принимающий аргументов; — конструктор, принимающий в качестве аргумента объект того же класса (или ссылку из него);

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

Конструктор по умолчанию

Конструктор не имеющий обязательных аргументов. Используется при создании массивов объектов, вызываясь для создания каждого экземпляра. В отсутствие явно заданного конструктора по умолчанию его код генерируется компилятором (что на исходном тексте, естественно, не отражается).

Конструктор копирования

Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функции по значению.

Конструктор копирования в основном необходим, когда объект имеет указатели на объекты выделенные в куче. Если программист не создаёт конструктор копирования, то компилятор создаст неявный конструктор копирования, который копирует указатели как есть, то есть фактическое копирование данных не происходит и два объекта ссылаются на одни и те же данные в куче. Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе.

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

Конструктор преобразования

Конструктор, принимающий один аргумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально.

Виртуальный конструктор

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

«Виртуальными конструкторами» называют похожий, но другой механизм, присутствующий в некоторых языках — например, он есть в Delphi, но нет в C++ и Java. Этот механизм позволяет создать объект любого заранее неизвестного класса при двух условиях:

  • этот класс является потомком некоего наперёд заданного класса (в данном примере это класс TVehicle );
  • на всём пути наследования от базового класса к создаваемому цепочка переопределения не обрывалась. При переопределении виртуального метода синтаксис Delphi требует ключевое слово overload , чтобы старая и новая функции с разными сигнатурами могли сосуществовать, override для переопределения функции либо reintroduce для задания новой функции с тем же именем — последнее недопустимо.

В языке вводится так называемый классовый тип (метакласс). Этот тип в качестве значения может принимать название любого класса, производного от TVehicle .

Такой механизм позволяет создавать объекты любого заранее неизвестного класса, производного от TVehicle .

Заметьте, что код

является некорректным — директива reintroduce разорвала цепочку переопределения виртуального метода, и в действительности будет вызван конструктор TMotorcycle.Create (а значит, будет создан мотоцикл, а не мопед!)

Синтаксис

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

Пример

Python

В языке Python конструктором является метод класса с именем __init__ . Кроме того не следует забывать, что первым аргументом любого метода должен быть указатель на контекст класса self.

Пример

Delphi

В Delphi, в отличие от C++, для объявления конструктора служит ключевое слово constructor . Имя конструктора может быть любым, но рекомендуется называть конструктор Create .

Пример

Некоторые отличия между конструкторами и другими методами Java:

  • конструкторы не имеют чётко определённого типа возвращаемых данных;
  • конструкторы не могут напрямую вызываться (необходимо использовать ключевое слово new );
  • конструкторы не могут быть synchronized , final , abstract , native и static типов;
  • конструкторы всегда выполняются в том же потоке.

Пример

JavaScript

В JavaScript в качестве конструктора выступает обычная функция, используемая в качестве операнда оператора new . Для обращения к созданному объекту используется ключевое слово this .

Пример

Пример

Пример

Эйфель

В Эйфеле подпрограммы, которые инициализируют объекты, называются процедурами создания. Процедуры создания в чём-то подобны конструкторам и в чём-то отличаются. Они имеют следующие характеристики:

  • Процедуры создания не имеют никакого явного типа результата возврата (по определению процедуры[Примечание 1] ).
  • процедуры создания поименованы (имена ограничены допустимыми идентификаторами);
  • процедуры создания задаются по именам в тексте класса;
  • процедуры создания могут быть вызваны напрямую (как обычные процедуры) для повторной инициализации объектов;
  • каждый эффективный (то есть конкретный, не абстрактный) класс должен (явно или неявно) указать по крайней мере одну процедуру создания;
  • процедуры создания отвечают за приведение только что проинициализированного объекта в состояние, которое удовлетворяет инварианту класса [Примечание 2] .

Хотя создание объекта является предметом некоторых тонкостей [Примечание 3] , создание атрибута с типовым объявлением x: T , выраженном в виде инструкции создания create x.make состоит из следующей последовательности шагов:

  • создать новый непосредственный экземпляр типа T [Примечание 4] ;
  • выполнить процедуру создания make для вновь созданного экземпляра;
  • прикрепить вновь созданный объект к сущности x .

Пример

В первом отрывке ниже определяется класс POINT . Процедура make кодируется после ключевого слова feature .

Ключевое слово create вводит список процедур, которые могут быть использованы для инициализации экземпляров класса. В данном случае список содержит default_create , процедуру с пустой реализацией, унаследованной из класса ANY , и процедуру make с реализацией в самом классе POINT .

Во втором отрывке класс, являющийся клиентом класса POINT , имеет объявления my_point_1 и my_point_2 типа POINT .

В коде подпрограммы my_point_1 создаётся с координатами (0.0; 0.0). Поскольку в инструкции создания не указана процедура создания, используется процедура default_create , унаследованная из класса ANY . Эта же строка могла бы быть переписана как create my_point_1.default_create . Только процедуры, указанные как процедуры создания могут использоваться в инструкциях создания (то есть в инструкциях с ключевым словом create ).

Следующей идёт инструкция создания для my_point_2 , задающая начальные значения для координат my_point_2 .

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

ColdFusion

Пример

Необходимо отметить, что в ColdFusion не существует метода-конструктора. Широкое распространение среди сообщества программистов на ColdFusion получил способ вызова метода ‘ init ‘, выступающего в качестве псевдоконструктора.

Пример

В PHP (начиная с версии 5) конструктор — это метод __construct() , который автоматически вызывается ключевым словом new после создания объекта. Обычно используется для выполнения различных автоматических инициализаций, как например, инициализация свойств. Конструкторы также могут принимать аргументы, в этом случае, когда указано выражение new , необходимо передать конструктору формальные параметры в круглых скобках.

Тем не менее, конструктор в PHP версии 4 (и ранее) — метод класса с именем этого же класса.

Пример

В Perl конструктор должен применить функцию bless к некой переменной (обычно ссылке на хеш):

Но это минимальный базовый вариант, есть множество более продвинутых способов, начиная от use fields и заканчивая Moose.

Упрощенные конструкторы (с псевдокодом)

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

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

Примечания

  1. ↑Подпрограммы Эйфеля являются либо процедурами либо функциями. У процедур нет никакого возвращаемого типа. Функции всегда имеют возвращаемый тип.
  2. ↑ Поскольку должен быть также удовлетворён инвариант наследуемого(-ых) класса(-ов), нет обязательного требования вызова родительских конструкторов.
  3. ↑ Полная спецификация содержится в стандартах ISO/ECMA по языку программироная Эйфель в он-лайн доступе. [2]
  4. ↑ Стандарт Эйфеля требует, чтобы поля были инициализированы при первом доступе к ним, т.ч. нет необходимости осуществлять их инициализацию значениями по умолчанию во время создания объекта.

Ссылки

  1. ↑ Конечно, это приводит к определённым техническим трудностям — например, что будет, если из конструктора выпадет исключение? Впрочем, разработчик класса просто должен выполнять требования языка, а в большинстве программ не требуется детальная диагностика и автоматические повторы при ошибках.
  2. ↑ISO/ECMA документ описания Эйфеля

См. также

Wikimedia Foundation . 2010 .

Полезное

Смотреть что такое «Конструктор (программирование)» в других словарях:

Конструктор — (от лат. constructor «строитель»): В Викисловаре есть статья «кон … Википедия

Конструктор класса — В объектно ориентированном программировании конструктор класса (от англ. constructor, иногда сокращают ctor) специальный блок инструкций, вызываемый при создании объекта, причём или при его объявлении (располагаясь в стеке или в статической… … Википедия

Конструктор объекта — В объектно ориентированном программировании конструктор класса (от англ. constructor, иногда сокращают ctor) специальный блок инструкций, вызываемый при создании объекта, причём или при его объявлении (располагаясь в стеке или в статической… … Википедия

Деструктор (программирование) — Деструктор специальный метод класса, служащий для деинициализации объекта (например освобождения памяти). Содержание 1 Деструктор в Delphi 2 Деструктор в С++ … Википедия

Класс (программирование) — У этого термина существуют и другие значения, см. Класс. Класс в программировании набор методов и функций. Другие абстрактные типы данных метаклассы, интерфейсы, структуры, перечисления характеризуются какими то своими, другими… … Википедия

Класс (объектно-ориентированное программирование) — Класс, наряду с понятием «объект», является важным понятием объектно ориентированного подхода в программировании (хотя существуют и бесклассовые объектно ориентированные языки, например, Прототипное программирование). Под классом подразумевается… … Википедия

Объектно-ориентированное программирование на Python — Объектно ориентированное программирование на Python программирование на Python с использованием парадигмы ООП: с самого начала Python проектировался как объектно ориентированный язык программирования[1]. Содержание 1 Введение 1.1 … Википедия

Объектно-ориентированное программирование на Питоне — С самого начала Питон проектировался как объектно ориентированный язык программирования [1]. Содержание 1 Введение 1.1 Принципы ООП … Википедия

Интерфейс (объектно-ориентированное программирование) — У этого термина существуют и другие значения, см. Интерфейс (значения). Интерфейс (от лат. inter «между», и face «поверхность») семантическая и синтаксическая конструкция в коде программы, используемая для специфицирования… … Википедия

Визуальное программирование — Возможно, эта статья содержит оригинальное исследование. Добавьте ссылки на источники, в противном случае она может быть выставлена на удаление. Дополнительные сведения могут быть на странице обсуждения. (25 мая 2011) … Википедия

В объектно-ориентированном программировании конструктор класса (от англ. constructor , иногда сокращают ctor) — специальный блок инструкций, вызываемый при создании объекта, причём или при его объявлении (располагаясь в стеке или в статической памяти, что допустимо в C++, но не в куче при использовании ключевого слова new .

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

Термин конструктор также используется для обозначения одного из тегов, описывающих данные в алгебраическом типе данных. Это использование несколько отличается от описываемого в статье. Для дополнительной информации смотрите Алгебраический тип данных.

В большинстве языков конструктор может быть перегружен, что позволяет использовать несколько конструкторов в одном классе, причём каждый конструктор может иметь различные параметры. Некоторые языки различают несколько особых типов конструкторов:

    — конструктор, не принимающий аргументов. — конструктор, принимающий в качестве аргумента объект того же класса (или ссылку из него).

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

Содержание

Виды конструкторов

Конструктор по умолчанию

Конструктор не имеющий обязательных аргументов. Используется при создании массивов объектов, вызываясь для создания каждого экземпляра. В отсутствие явно заданного конструктора по умолчанию его код генерируется компилятором (что на исходном тексте, естественно, не отражается).

Конструктор копирования

Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функции по значению.

Конструктор копирования нужен, например, если для хранения данных объекта требуется дополнительно выделяемая память. Если его не будет, то конструктором копирования (сгенерированным компилятором) будут скопированы указатели, адресующие данные прежнего объекта (без выделения новой памяти). Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе.

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

Конструктор преобразования

Конструктор, принимающий один агрумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально.

Виртуальный конструктор

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

  • Этот класс является потомком некоего наперёд заданного класса (в данном примере это класс TVehicle )
  • На всём пути наследования от базового класса к создаваемому цепочка переопределения не обрывалась (при переопределении виртуального метода синтаксис Delphi требует ключевое слово override для переопределения функции либо reintroduce для задания новой функции с тем же именем).

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

Такой механизм позволяет создавать объекты любого заранее неизвестного класса, производного от TVehicle .

Заметьте, что код

является некорректным — директива reintroduce разорвала цепочку переопределения виртуального метода, и в действительности будет вызван конструктор TMotorcycle.Create (а значит, будет создан мотоцикл, а не мопед!)

Синтаксис

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

Python

В языке Python конструктором является метод класса с именем __init__ . Кроме того не следует забывать, что первым аргументом любого метода должен быть указатель на контекст класса self.

Пример

Delphi

В C++, для объявления конструктора служит ключевое слово constructor . Имя конструктора может быть любым, но рекомендуется называть конструктор Create .

Пример

  • Конструкторы не имеют чётко определённого типа возвращаемых данных.
  • Конструкторы не могут напрямую вызываться (необходимо использовать ключевое слово new ).
  • Конструкторы не могут быть synchronized, final, abstract, native и static типов.
  • Конструкторы всегда выполняются в том же потоке.

Пример

Конструкторы в Visual Basic используют обычный метод объявления с именем New .

Пример

Пример

Eiffel

В Эйфеле подпрограммы, которые инициализируют объекты, называются процедурами создания. Процедуры создания в чём-то подобны конструкторам и в чём-то отличаются. Они имеют следующие характеристики:

  • Процедуры создания не имеют никакого явного типа результата возврата (по определению процедуры[Примечание 1] ).
  • Процедуры создания поименованы. Имена ограничены допустимыми идентификаторами.
  • Процедуры создания задаются по именам в тексте класса.
  • Процедуры создания могут быть вызваны напрямую (как обычные процедуры) для повторной инициализации объектов.
  • Каждый эффективный (т.е. конкретный, не абстрактный) класс должен (явно или неявно) указать по крайней мере одну процедуру создания.
  • Процедуры создания отвечают за приведение только что проинициализированного объекта в состояние, которое удовлетворяет инварианту класса [Примечание 2] .

Хотя создание объекта является предметом некоторых тонкостей [Примечание 3] , создание аттрибута с типовым объявлением x: T , выраженном в виде инструкции создания create x.make состоит из следующей последовательности шагов:

  • Создать новый непосредственный экземпляр типа T [Примечание 4] .
  • Выполнить процедуру создания make для вновь созданного экземпляра.
  • Прикрепить вновь созданный объект к сущности x .

Пример

В первом отрывке ниже определяется класс POINT . Процедура make кодируется после ключевого слова feature .

Ключевое слово create вводит список процедур, которые могут быть использованы для инициализации экземпляров класса. В данном случае список содержит default_create , процедуру с пустой реализацией, унаследованной из класса ANY , и процедуру make с реализацией в самом классе POINT .

Во втором отрывке класс, являющийся клиентом класса POINT , имеет объявления my_point_1 и my_point_2 типа POINT .

В коде подпрограммы my_point_1 создаётся с координатами (0.0; 0.0). Поскольку в инструкции создания не указана процедура создания, используется процедура default_create , унаследованная из класса ANY . Эта же строка могла бы быть переписана как create my_point_1.default_create . Только процедуры, указанные как процедуры создания могут использоваться в инструкциях создания (т.е. в инструкциях с ключевым словом create ).

Следующей идёт инструкция создания для my_point_2 , задающая начальные значения для координат my_point_2 .

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

Пример

Необходимо отметить, что в ColdFusion не существует метода-конструктора. Широкое распространение среди сообщества программистов на ColdFusion получил способ вызова метода ‘ init ‘, выступающего в качестве псевдоконструктора.

Пример

В PHP (начиная с версии 5) конструктор — это метод __construct() , который автоматически вызывается ключевым словом new после создания объекта. Обычно используется для выполнения различных автоматических инициализаций, как например, инициализация свойств. Конструкторы также могут принимать аргументы, в этом случае, когда указано выражение new , необходимо передать конструктору формальные параметры в круглых скобках.

Тем не менее, конструктор в PHP версии 4 (и ранее) — метод класса с именем этого же класса.

Упрощенные конструкторы (с псевдокодом)

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

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

Примечания

  1. ↑Подпрограммы Эйфеля являются либо процедурами либо функциями. У процедур нет никакого возвращаемого типа. Функции всегда имеют возвращаемый тип.
  2. ↑ Поскольку должен быть также удовлетворён инвариант наследуемого(-ых) класса(-ов), нет обязательного требования вызова родительских конструкторов.
  3. ↑ Полная спецификация содержится в стандартах ISO/ECMA по языку программироная Эйфель в он-лайн доступе. [1]
  4. ↑ Стандарт Эйфеля требует, чтобы поля были инициализированы при первом доступе к ним, т.ч. нет необходимости осуществлять их инициализацию значениями по умолчанию во время создания объекта.

Ссылки

См. также

Wikimedia Foundation . 2010 .

Полезное

Смотреть что такое «Конструктор объекта» в других словарях:

конструктор класса — Специальный блок инструкций, вызываемый при создании объекта. [ГОСТ Р 54456 2011] Тематики телевидение, радиовещание, видео EN class constructor … Справочник технического переводчика

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

Конструктор класса — В объектно ориентированном программировании конструктор класса (от англ. constructor, иногда сокращают ctor) специальный блок инструкций, вызываемый при создании объекта, причём или при его объявлении (располагаясь в стеке или в статической… … Википедия

Конструктор копирования — Конструктором копирования (в англоязычной литературе используется термин copy constructor) называется специальный конструктор в языке программирования C++, применяемый для создания нового объекта как копии уже существующего. Такой конструктор… … Википедия

Класс объекта — Класс, наряду с понятием «объект», является важным понятием объектно ориентированного подхода в программировании (хотя существуют и бесклассовые объектно ориентированные языки, например, Прототипное программирование). Под классом подразумевается… … Википедия

Нуль-арный конструктор — В компьютерном программировании нуль арным конструктором (в англ. языке используется термин nullary constructor) называют конструктор, не принимающий аргументы. Содержание 1 Объектно ориентированные конструкторы 1.1 … Википедия

C++11 — C++11[1][2] или ISO/IEC 14882:2011[3] (в процессе работы над стандартом носил условное наименование C++0x[4][5]) новая версия стандарта языка C++, вместо ранее действовавшего ISO/IEC 14882:2003. Новый стандарт включает дополнения в ядре… … Википедия

C++0x — C++0x будущая версия стандарта языка C++, вместо ныне существующего ISO/IEC 14882:2003. Новый стандарт будет включать дополнения в ядре языка и расширение STL, включая большую часть TR1 кроме, вероятно, библиотеки специальных… … Википедия

TObject — TObject класс, являющийся общим предком всех классов языка Object Pascal. См. также: Free Pascal. TObject инкапсулирует основное поведение всех классов в Object Pascal и отвечает за выделение и освобождение памяти при создании и удалении… … Википедия

C++ — У этого термина существуют и другие значения, см. C. См. также: Си (язык программирования) C++ Семантика: мультипарадигмальный: объектно ориентированное, обобщённое, процедурное, метапрограммирование Тип исполнения: компилируемый Появился в … Википедия

Читайте также:

      

  • Первые деревянные кубики лего
  •   

  • Башня рапунцель лего детский мир
  •   

  • Как сделать из лего формулу один
  •   

  • Конструктор lego super heroes разгром халкбастера 76031
  •   

  • Детский мир лего феррари

Понравилась статья? Поделить с друзьями:
  • Сантомектин инструкция по применению в ветеринарии для коз
  • От лица руководства клиники
  • Как поменять снилс при смене фамилии через госуслуги пошаговая инструкция
  • Vektor jsd 20 w инструкция по применению
  • Полин таблетки инструкция по применению для чего применяется взрослым