Конструктор (объектно-ориентированное программирование)
Из Википедии, бесплатной энциклопедии
В объектно-ориентированном программировании конструктор класса (от англ. constructor) — специальный блок инструкций, вызываемый при создании объекта.
Назначение конструктора
[править | править код]Возможно, этот раздел содержит оригинальное исследование. |
Одна из ключевых особенностей ООП — инкапсуляция: внутренние поля класса напрямую недоступны, и пользователь может работать с объектом только как с единым целым, через открытые (public
) методы. Каждый метод, в идеале, должен быть устроен так, чтобы объект, находящийся в «допустимом» состоянии (то есть когда выполняется инвариант класса), после вызова метода также оказался в допустимом состоянии. И первая задача конструктора — перевести поля объекта в такое состояние.
Вторая задача — упростить пользование объектом. Объект — не «вещь в себе», ему часто приходится требовать какую-то информацию от других объектов: например, объект File
, создаваясь, должен получить имя файла. Это можно сделать и через метод:
File file; file.open("in.txt", File::omRead);
Но удобнее открытие файла сделать в конструкторе:[1]
File file("in.txt", File::omRead);
Виды конструкторов
[править | править код]Разнообразные языки программирования представляют несколько разновидностей конструкторов:
- конструктор с параметрами;
- конструктор по умолчанию, не принимающий аргументов;
- именованный конструктор — функция, предполагающая явный вызов по имени, работающая как конструктор
- конструктор копирования — конструктор, принимающий в качестве аргумента объект того же класса (или ссылку из него);
- конструктор преобразования — конструктор, принимающий один аргумент (эти конструкторы могут вызываться автоматически для преобразования значений других типов в объекты данного класса).
- конструктор перемещения (специфично для C++11)
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; };
Конструктор с параметрами
[править | править код]Конструкторы, принимающие один или более аргументов, называются параметризованными. Например:
class Example { int x, y; public: Example(); Example(int a, int b); // параметризованный конструктор }; Example :: Example() { } Example :: Example(int a, int b) { x = a; y = b; }
Параметризованный конструктор может быть вызван явно или неявно, например:
Example e = Example(0, 50); // явный вызов Example e(0, 50); // неявный вызов
Конструктор по умолчанию
[править | править код]Конструктор, не имеющий обязательных аргументов. Используется при создании массивов объектов, вызываясь для создания каждого экземпляра. В отсутствие явно заданного конструктора по умолчанию его код генерируется компилятором (что на исходном тексте, естественно, не отражается).
Именованный конструктор
[править | править код]Это пустой раздел, который еще не написан. |
Конструктор копирования
[править | править код]Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функции по значению.
Конструктор копирования в основном необходим, когда объект имеет указатели на объекты, выделенные в куче. Если программист не создаёт конструктор копирования, то компилятор создаст неявный конструктор копирования, который копирует указатели как есть, то есть фактическое копирование данных не происходит и два объекта ссылаются на одни и те же данные в куче. Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе.
Аргумент должен передаваться именно по ссылке, а не по значению. Это вытекает из коллизии: при передаче объекта по значению (в частности, для вызова конструктора) требуется скопировать объект. Но для того, чтобы скопировать объект, необходимо вызвать конструктор копирования.
Конструктор преобразования
[править | править код]Конструктор, принимающий один аргумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально.
Определенное пользователем преобразование типа может иметь одну из двух форм: - из классового типа C в любой тип T, для чего у С должен быть C::operator T() - из любого типа T в классовый тип C, для чего у C должен быть C::C(T) (или же C::C(T&), или же C::C(T&&))
Если в каком-то выражении допустимы оба этих случая, возникает неоднозначность и ошибка компиляции.
Если конструктор (или operator T()) помечен ключевым словом explicit, то такое преобразование типа применяется только при наличии явной операции приведения типа вида (T)C или же static_cast<T>C. Если же слова explicit нет, то компилятор может вставить такое преобразование даже неявно, например, при вызове функции f(T arg) в виде f(C).
Конструктор перемещения
[править | править код]В C++11 появился новый тип неконстантных ссылок, носящий название «ссылка на праводопустимое выражение» (англ. rvalue reference) и обозначаемый как T&&
, и новый вид конструкторов — конструкторы перемещения (англ. move constructors). Конструктор перемещения принимает на входе значение неконстантной ссылки на объект класса, и используется для передачи владения ресурсами этого объекта. Конструкторы перемещения были придуманы для решения проблемы потери эффективности, связанной с созданием временных объектов.
Виртуальный конструктор
[править | править код]Конструктор не бывает виртуальным в смысле виртуального метода — для того, чтобы механизм виртуальных методов работал, нужно запустить конструктор, который автоматически настроит таблицу виртуальных методов данного объекта.
«Виртуальными конструкторами» называют похожий, но другой механизм, присутствующий в некоторых языках — например, он есть в 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
(а значит, будет создан мотоцикл, а не мопед!)
См. также Фабрика (шаблон проектирования)
Синтаксис
[править | править код]C++
[править | править код]Имя конструктора должно совпадать с именем класса. Допускается использовать несколько конструкторов с одинаковым именем, но различными параметрами.
Пример
[править | править код]class ClassWithConstructor { public: /* Инициализация внутреннего объекта с помощью конструктора */ ClassWithConstructor(float parameter): object(parameter) {}/* вызов конструктора AnotherClass(float); */ private: AnotherClass object; };
Python
[править | править код]В языке Python конструктор состоит из двух методов класса: __new__
и __init__
. Метод __new__
создаёт объект, а метод __init__
инициализирует объект.
Пример
[править | править код]class ClassWithConstructor: def __new__(cls): """This method is the first part of the constructor.""" return super().__new__(cls) def __init__(self): """This method is the second part of the constructor.""" pass
Ruby
[править | править код]В языке Ruby, чтобы задать объекту первоначальное непротиворечивое состояние, используется специальный метод initialize
.
Пример
[править | править код]class ClassWithConstructor def initialize print 'This method is constructor.' end end
Delphi
[править | править код]В Delphi, в отличие от C++, для объявления конструктора служит ключевое слово constructor
. Имя конструктора может быть любым, но рекомендуется называть конструктор Create
.
Пример
[править | править код] TClassWithConstructor = class public constructor Create; end;
Java
[править | править код]Некоторые отличия между конструкторами и другими методами Java:
- конструкторы не имеют типа возвращаемых данных (на самом деле они всегда возвращают this);
- конструкторы не могут напрямую вызываться (необходимо использовать ключевое слово
new
); - конструкторы не могут иметь модификаторы
synchronized
,final
,abstract
,native
иstatic
;
Пример
[править | править код]public class Example { private int data; // Конструктор по умолчанию, data инициализируется 1, при создании экземпляра класса Example public Example() { data = 1; } // Перегрузка конструктора public Example(int input) { data = input; } }
// код, иллюстрирующий создание объекта описанным выше конструктором Example e = new Example(42);
JavaScript
[править | править код]В JavaScript в качестве конструктора выступает обычная функция, используемая в качестве операнда оператора new
. Для обращения к созданному объекту используется ключевое слово this
.
Однако в спецификации ECMAScript 6 были добавлена синтаксическая оболочка прототипов, которой присущи такие свойства ООП как наследование, а также небольшой список обязательных методов, например: toString()
.
Пример
[править | править код]function Example(initValue) { this.myValue = initValue; } Example.prototype.getMyValue = function() { return this.myValue; } //ES6 class class Example { constructor() { console.log('constructor'); } }
// код, иллюстрирующий создание объекта описанным выше конструктором 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 _number; private string _string; public MyClass(int num, string str) { _number = num; _string = str; } }
// Код, иллюстрирующий создание объекта описанным выше конструктором MyClass example = 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) { // ... здесь храним вводимые данные и прочие внутрнние поля ... } // ... }
См. также
[править | править код]Примечания
[править | править код]- ↑ Подпрограммы Эйфеля являются либо процедурами либо функциями. У процедур нет никакого возвращаемого типа. Функции всегда имеют возвращаемый тип.
- ↑ Поскольку должен быть также удовлетворён инвариант наследуемого(-х) класса(-ов), нет обязательного требования вызова родительских конструкторов.
- ↑ Полная спецификация содержится в стандартах ISO/ECMA по языку программироная Эйфель в онлайн доступе.[2]
- ↑ Стандарт Эйфеля требует, чтобы поля были инициализированы при первом доступе к ним, т.ч. нет необходимости осуществлять их инициализацию значениями по умолчанию во время создания объекта.
Ссылки
[править | править код]- ↑ Конечно, это приводит к определённым техническим трудностям — например, что будет, если из конструктора выпадет исключение? Впрочем, разработчик класса просто должен выполнять требования языка, а в большинстве программ не требуется детальная диагностика и автоматические повторы при ошибках.
- ↑ ISO/ECMA документ описания Эйфеля . Дата обращения: 19 апреля 2009. Архивировано 16 июня 2008 года.
![]() | В статье не хватает ссылок на источники (см. рекомендации по поиску). |