Конструктор (объектно-ориентированное программирование)
В объектно-ориентированном программировании конструктор класса (от англ. 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 конструктором является метод класса с именем __init__
. Кроме того не следует забывать, что первым аргументом любого метода должен быть указатель на контекст класса self
.
Пример
[править | править код]class ClassWithConstructor:
def __init__(self):
"""This method is constructor."""
pass
Ruby
[править | править код]В языке Ruby, чтобы задать объекту первоначальное непротиворечивое состояние, используется специальный метод initialize
.
Пример
[править | править код]class ClassWithConstructor
def initialize
print 'This method is constructor.'
end
end
Delphi
[править | править код]В Delphi, в отличие от C++, для объявления конструктора служит ключевое слово constructor
. Имя конструктора может быть любым, но рекомендуется называть конструктор Erstellen Sie
.
Пример
[править | править код] 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 года.
![]() | В статье не хватает ссылок на источники (см. рекомендации по поиску). |