Конструктор (программирование)

Материал из Википедии — свободной энциклопедии
(перенаправлено с «Конструктор объекта»)
Перейти к: навигация, поиск

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

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

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

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

Содержание

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

Одна из ключевых особенностей ООП — инкапсуляция: внутренние поля объекта напрямую недоступны, и пользователь может работать с объектом только как с единым целым, через открытые (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 документ описания Эйфеля

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


Личные инструменты
Пространства имён

Варианты
Действия
Навигация
Участие
Печать/экспорт
Инструменты
На других языках