Короче, поштудировал в эти выходные первоисточники, так сказать. Недопонимание между мной и prospero78 было вызвано тем, что я использовал понятие конструктора, данное Мёссенбёком, а он, видимо, привычное понятие из мейнстримных языков. У Мёссенбёка конструктор
выделяет память под объект и инициализирует его, а вот в мейнстриме преимущественно
только инициализирует. И ветер конструкторов, которые только инициализируют, похоже, дует с С++.
СимулаООП гражданин Строструп добавил в С, взяв за основу язык Симула. В Симуле понятия конструктора вообще не было, там экземпляр класс (объект)
порождается (is generated) как результат вычисления значения выражения, известного как
генератора объекта (object generator) и определённого следующим образом:
Код:
<object generator> ::=
new <class identifier><actual parameter part>
Тут, правда, нужно понимать, что в Симуле класс - это всего-навсего разновидность процедуры, правда особая разновидность. Такая, которая определяет блок кода, время жизни экземпляров которого не ограничено вызовом этой процедуры. Поэтому синтаксически определение класса в Симуле выглядит почти так же, как определение процедуры, только начинается с нового зарезервированного слова class, а потом как для процедуры: идентификатор, список формальных параметров, и т.д.
Код:
<class declaration> ::= <prefix><main part>
<main part> ::= class <class identifier>
<formal parameter part>;
<value part><specification part>
<virtual part><class body>
И создание экземпляра класса синтаксически лишь чуточку отличается от создания экземпляра обычной процедуры (путём её вызова): в генераторе объекта присутствует новое зарезервированное слово new. А так, всё как при вызове процедуры: значения фактических параметров используются для инициализации значений формальных параметров.
С++ и (почти) все-все-всеВ С++ классы не имеют никакого отношения к процедурам, они родственны структурам в языке С. Но синтаксис создания экземпляра класса таки скалькирован из Симулы: new, а затем... А вот что затем? В Симуле было имя класса, который суть есть процедура, и список фактических параметров. Но ведь в С++ то класс уже не процедура.
Значит, нужно добавить какую-ту процедуру в класс, которая будет использоваться при создании объекта с той же целью, с которой в Симула использовался сам класс: для инициализации значений членов класса С++, которые играют роль формальных параметров классов Симула. Вот это-ту процедуру Строструп по какой-то непонятной причине назвал конструктором (constructor), хотя куда более подходящий термин инициализатор был бы много прозрачнее. Отмечу мимоходом, что уже упомянутое прямое калькирование идиомы порождения объекта из Симулы и привело к тому небезызвестному факту, что конструктор в С++ имеет тоже самое имя что и класс.
Но тут есть нюанс: строго говоря, конструктор в С++ - это не процедура, т.к. у него нет возвращаемого значения. Поэтому и вызывать его, как обычную процедуру нельзя*, нужно использовать специальную конструкцию, скалькированную с Симулы,
Код:
Foo* f = new Foo(param);
Ну а дальше это дело разошлось: в любом языке, "срисованном" с С++ (см. Java, C#), процедуру, инициализирующую объект продолжали именовать конструктором.
Но таки не все...Но есть и контр-примеры из мейнстрима, уже приведённые в обсуждении:
- Иван Кузьмицкий упоминал Go, в котором нет конструкторов, и приводил ссылку, в которой паттерн конструктор реализован в точности, как у Мёссенбёка: по схеме alloc-init-return. Вот ещё, пожалуйста, обсуждение этого вопроса со Stack Overflow: все тот же шаблон alloc-init-return и никого не смущает, что в С++ и иже с ними конструктор только инициализирует: при его вызове сначала выделяется память, а уже потом происходит инициализация, как описано в конструкторе.
- Ну и я приводил в пример Objective-C, в котором составные части конструктора названы своими именами (alloc, init).
Суть, в том, что имеется два подхода.
- Либо в языке должны быть специальные синтаксические конструкции как для определения, так и для вызова конструктора "в стиле С++", так как такой конструктор - это не процедура (он ничего не возвращает), а следовательно и объявлять, и вызывать его нужно как-то по-особому.
- Либо можно организовать реализацию паттерна конструктор "по-Мёссенбёку" обычной процедурой, которая вернёт ссылку на созданные объект. В нутрях процедуры будет, естественно, alloc-init-return.
Весь вопрос только в том, позволяет ли подход Мёссенбёка делать всё то, что могут конструкторы "в стиле С++"? Сходу сказать не могу, поздно же, котелок не варит.
------
*) На самом деле синтаксически то в С++ можно написать выражение, в котором конструктор вызывается как обычная функция (когда создаёте объект "на стеке"),
Код:
Foo f = Foo(param);
но эту дичь можно проигнорировать, ведь это выражение только выглядит как вызов функции, но на самом деле таковым не является.