Конструктор ооп что это

Обновлено: 28.03.2024

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

Любая программа, написанная на языке ООП, отражает в своих данных состояние физических предметов либо абстрактных понятий – объектов программирования, для работы, с которыми она предназначена.

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

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

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

Класс – это описание множества объектов программирования (объектов) и выполняемых над ними действий.

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

Основные понятия объектно-ориентированного программирования

Любая функция в программе представляет собой метод для объекта некоторого класса.

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

Вся программа в таком виде представляет собой объект некоторого класса с единственным методом run (выполнить).

Программирование «от класса к классу» включает в себя ряд новых понятий. Основными понятиями ООП являются

  • инкапсуляция;
  • наследование;
  • полиморфизм.

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

Внутри объекта код и данные могут быть закрытыми или открытыми.

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

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

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

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

Иначе говоря, новый класс наследует как данные старого класса, так и методы их обработки.

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

Пример наследования – определение структуры, отдельный член которой является ранее определенной структурой.

Наследование структур


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

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

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

Например, нахождение абсолютной величины в языке Си требует трех разных функций с разными именами:

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

Сегодня хочу поговорить про ООП(объектно-ориентированное программирование).
А именно ООП в JavaScript.

Небольшое лирическое отступление:

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

Изучая конкретный язык программирования, каждый должен знать, как в нём поддерживается ООП, вернее не так, как писать объектно-ориентированный код.

ООП — способ организации представления данных в виде классов, а те, в свою очередь, — объектов. Методология проектирования данных.

Так, давайте по порядку.

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

Вот ещё парочка примеров:

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

Так, думаю, теоретически Вы поняли самую главную суть ООП. Теперь давайте посмотрим… стоп! Что-то я углубился, судя по заголовку статьи, мы должны рассмотреть как это всё устроенно в JS. Ну что же давайте посмотрим.

Всем уже должно быть известно как определяется объект в JS. Вы скажите, с помощью литералов и это так! То есть, например.


Ок, это понятно, а как тогда классы!? В JS нету классов! Ага, вообще.
Ну, ладно, есть, только не в явном виде. Это как? Очень просто.
Наверное, Вы слышали, что всё в JS — объекты, так? Конечно, слышали.

Так вот функции это тоже объекты. То есть у них тоже есть свойства, и даже можно определять функции в качестве свойства, тем самым образуя ЗАМЫКАНИЯ, но это тема другой статьи, поэтому не будем заострять на этом внимание.

У всех классов есть конструкторы, то есть функции, которые инициализируют объект, описывают все(или не все)

Если попытаться обратиться к свойству name через функцию Person, и вывести его значение в консоль вот так:

то получим undefined!

Но почему? Дело в том, что this не на что ссылаться. Об этом чуть позже. Нужно создать объект, иначе как экземпляр функции-конструктора Person. Для этого нам понадобиться оператор new.


Ок, создали, теперь если обратиться к свойству name по только что созданному объекту objPerson

Но как создаётся объект на самом деле? Давайте разбираться.

Вот алгоритм интерпретатора JS, встретив на своём пути оператор new

  1. ищет объявление функции-конструктора, название которой указано сразу после оператора new в конструкторе
  2. переменная this становиться равна новому созданному объекту, в данном случае objPerson

То есть теперь переменная objPerson ссылается на экземпляр функции Person(), то есть конструктор.

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

Да, но она предназначена для вызова некоторого фрагмента кода. Просто она сама по себе является объектом.

А теперь вот полноценный пример:

Свойство constructor

Теперь давайте подробнее рассмотрим создание объекта класса Person.

У каждой функции есть свойство prototype, которое хранит в себе объект следующего вида:


И при создании объекта через new интерпретатор, сразу после выполнения ранее описанного алгоритма преобразует итоговое значение в ClassName.prototype, в данном случаи — Person.prototype.


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


Если попробовать обратиться к этому свойству через objPerson, то мы получим код объявления функции-конструктора Person

Знакомьтесь, Наследование

Один из китов, на котором держится ООП.

Вот краткое определение:

это возможность переопределять свойства одного класса другим.
Хм…и всё!? На самом деле, да, то есть основный принцип наследования — это переопределить общую характеристику класса с добавлением чего-то уникального для конкретного класса.

Давайте приведу аналогию

Допустим, что у нас есть чертёж дома(класс будущего объекта), у этого дома будет, например, сколько-то: этажей, комнат; и возможное наличие подвала.
Но потом Вы нарисовали новый чертёж для дома, в нём будут присущи все остальные характеристики, что у предыдущего, но с несколькими различиями, например, возможное наличие чердака, веранды.

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

Давайте теперь посмотрим как это всё выглядит в коде. Вот у нас есть класс, функция-конструктор House


А вот реализация класса ModernHouse


Ок, мы описали два класса, из которых второй переопределяет реализацию первого с помощью вот такого выражения House.apply(this, arguments) — искренне надеюсь, что Вы знаете, что это такое, но тем не менее, читатель может и не знать, поэтому краткое описание:

С помощью функции apply() мы можем вызывать различные функции в определенном контексте, указанном в первом параметре, в данном случаи this, а также указать аргументы в виде массива.

Помимо функции apply, существует ещё call, разница которой относительно apply — она принимает в качестве аргументов отдельные значения, разделённые запятой.

Например, House.call(this, ctFloors, ctRooms, isBasement) — эквивалентное выражение House.apply(this, arguments);

Теперь же давайте попробуем создать объект класса ModernHouse и обратиться к какому-нибудь свойству.


Если вспомнить алгоритм интерпретатора JS, то переменная modern будет хранить значение следующего вида ModernHouse.prototype, а то в свою очередь литерал объекта:

Знакомьтесь, Инкапсуляци

Инкапсуляция — если кратко, приватизация данных, то есть отделение от общедоступных данных в определенную оболочку, доступ к которой происходит через общедоступные функции.

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


Мы хотим, чтоб обращение к свойству rows, cols не было, то есть было запрещено, или попросту не срабатывало.

Для этого нужно немного, просто объявить эти свойства как переменные через var.


undefined! Да, то, что нам нужно! Так как наши переменные локальны, поэтому обращение к ним из других областей видимости попросту невозможно!

Но как же теперь получить значение этих свойств!? Для этого нужно объявить так называемые функции-геттеры и -сеттеры.


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

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

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

date today = date(6,4,2014); // полная форма
date xmas(25,12,0); // сокращенная форма
// date my_burthday; // недопустимо, опущена инициализация

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

class date <
int month, day, year;
public :
date( int , int , int ); // день месяц год
date( char *); // дата в строковом представлении
date(); // дата по умолчанию: сегодня
>;

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

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

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

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

class date
int month, day, year;
public :
date( int , int , int );
date( char *);
date(); // конструктор по умолчанию
>;

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

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

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

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

  • date2 в приведенном определении;
  • для создаваемого в стеке формального параметра;
  • для временного объекта, сохраняющего значение, возвращаемое функцией.

Вместо этого в них копируется содержимое объекта-источника:

  • date1 в приведенном примере;
  • фактического параметра;
  • объекта-результата в операторе return .

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

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

Деструкторы

Определяемый пользователем класс имеет конструктор, который обеспечивает надлежащую инициализацию. Для многих типов также требуется обратное действие. Деструктор обеспечивает соответствующую очистку объектов указанного типа. Имя деструктора представляет собой имя класса с предшествующим ему знаком «тильда» ~ . Так, для класса X деструктор будет иметь имя ~X() . Многие классы используют динамическую память, которая выделяется конструктором, а освобождается деструктором.

class date
int day, year;
char *month;
public :
date( int d, char * m, int y)
day = d;
month = new char [strlen(m)+1];
strcpy_s(month, strlen(m)+1,m);
year = y;
>
~date() < delete [] month; >// деструктор
>;

Поля, имеющие тип класса

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

Конструктор нового класса имеет пустое тело и список вызываемых конструкторов класса vect , перечисленных после двоеточия (:) через запятую (,). Они выполняются с целым аргументом i , создавая 3 объекта класса vect: a, b, c .

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

Здравствуйте, у меня такой вопрос. В коде я не нашел сам деструктор. Он создается и запускается сам после выхода из блока main ?

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

Наследование

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


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

Для порождения нового класса на основе существующего используется следующая общая форма

При объявлении порождаемого класса МодификаторДоступа может принимать значения public , private , protected либо отсутствовать, по умолчанию используется значение private . В любом случае порожденный класс наследует все члены базового класса, но доступ имеет не ко всем. Ему доступны общие ( public ) члены базового класса и недоступны частные ( private ).

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

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

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

При общем наследовании порожденный класс имеет доступ к наследуемым членам базового класса с видимостью public и protected . Члены базового класса с видимостью private – недоступны.

Спецификация доступа внутри класса в порожденном классе вне класса
private +
protected + +
public + + +

Общее наследование означает, что порожденный класс – это подтип базового класса. Таким образом, порожденный класс представляет собой модификацию базового класса, которая наследует общие и защищенные члены базового класса.

Порожденный класс наследует все данные класса student (строка 13), имеет доступ к protected и public — членам базового класса. В новом классе добавлено два поля данных (строки 16, 17), и порожденный класс переопределяет функцию print() (строки 20, 39-43).

Конструктор для базового класса вызывается в списке инициализации (строка 29).

Но что происходит, когда мы присваиваем указателю класса student ссылку на объект класса grad_student (строка 55)? В этом случае происходит преобразование указателей, и в строке 56 вызывается уже функция print() класса student .


Указатель на порожденный класс может быть неявно передан в указатель на базовый класс. А указатель на порожденный класс может указывать только на объекты порожденного класса. То есть обратное преобразование недопустимо

Неявные преобразования между порожденным и базовым классами называются предопределенными стандартными преобразованиями :

  • объект порожденного класса неявно преобразуется к объекту базового класса.
  • ссылка на порожденный класс неявно преобразуется к ссылке на базовый класс.
  • указатель на порожденный класс неявно преобразуется к указателю на базовый класс.

Частное наследование

Порожденный класс может быть базовым для следующего порождения. При порождении private наследуемые члены базового класса, объявленные как protected и public , становятся членами порожденного класса с видимостью private . При этом члены базового класса с видимостью public и protected становятся недоступными для дальнейших порождений. Цель такого порождения — скрыть классы или элементы классов от использования их в дальнейших порождениях. При порождении private не выполняются предопределенные стандартные преобразования:

Однако порождение private позволяет отдельным элементам базового класса с видимостью public и protected сохранить свою видимость в порожденном классе. Для этого необходимо

  • в части protected порожденного класса указать те наследуемые члены базового класса с видимостью protected , уточненные именем базового класса, для которых необходимо оставить видимость protected и в порожденном классе;
  • в части public порожденного класса указать те наследуемые члены базового класса с видимостью public , уточненные именем базового класса, для которых необходимо оставить видимость public и в порожденном классе.

class X
private :
int n;
protected :
int m;
char s;
public :
void func( int );
>;
class Y : private X
private :
.
protected :
.
X::s;
public :
.
X::func();
>;

Возможен и третий вариант наследования – с использованием модификатора доступа protected .

Доступ к элементам базового класса из производного класса, в зависимости от модификатора наследования:

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

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

Если и у базового и у производного классов есть конструкторы и деструкторы, то конструкторы выполняются в порядке наследования, а деструкторы – в обратном порядке. То есть если А – базовый класс, В – производный из А , а С – производный из В ( А-В-С ), то при создании объекта класса С вызов конструкторов будет иметь следующий порядок:

  • конструктор класса А
  • конструктор класса В
  • конструктор класса С .

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

  • деструктор класса С
  • деструктор класса В
  • деструктор класса А .

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

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

КонструкторПроизводногоКласса (СписокФормальныхАргументов)
: КонструкторБазовогоКласса (СписокФактическихАргументов)
< // тело конструктора производного класса >

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

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

Здравствуйте! а подскажите, пожалуйста, как я могу объявить неполный наследуемый класс? Например, у меня есть class a b class b, наследуемый откласса а. я хочу неполно объявить класс b перед классом а. как это синтаксически сделать? нигде не могу найти инфу об этом.

В этой статье мы продолжим изучать объектно-ориентированное программирование. В прошлой статье мы рассказали: Что такое класс, инкапсуляция, полиморфизм, наследование. Сегодня мы узнаем: Что такое конструктор в объектно-ориентированном программировании и как его использовать при написании программ в ООП стиле.

В этой статье мы продолжим изучать объектно-ориентированное программирование. В прошлой статье мы рассказали: Что такое класс, инкапсуляция, полиморфизм, наследование. Сегодня мы узнаем: Что такое конструктор в объектно-ориентированном программировании и как его использовать при написании программ в ООП стиле.

Что такое конструктор

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

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


Давайте освежим знания и напишем простой класс на языке программирования PHP.

Пример простого класса:

Если выполнить программу, на мониторе вы увидите название модели автомобиля: car


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

Наш Car - пока еще не очень функциональный автомобиль. Он не умеет ездить, а знает только какая у него модель $model, цвет $color, максимальная скорость $maxSpeed и текущая скорость $currentSpeed.

Создадим еще один автомобиль:

Наш superCar ничем не отличается от обычного $car. И это очень плохо, так как нашему покупателю мы пообещали продать автомобиль с более высокими характеристиками, чем прошлый автомобиль.

Мы можем задать свойства автомобиля после создания экземпляра класса, но это будет выглядеть перед покупателем примерно так: Сейчас мы заменим двигатель, перекрасим, подождите пожалуйста часов 20, а еще лучше недельку. Думаю, что покупателю это не понравится и он уйдет из нашего автомобильного салона.

В объектно-ориентированном программировании принято инициализировать переменные при создании экземпляра класса. Для этого мы воспользуемся конструктором.

На языке программирования PHP пустой конструктор выглядит так:

Давайте встроим его в наш класс Car и создадим возможность присваивать переменным значения, при создании экземпляра класса:

А сейчас создадим сразу два автомобиля и зададим значения свойств при создании экземпляров классов:

Как вы видите, у обычного автомобиля (класс Car) мы задали модель = car, цвет = red, максимальную скорость = 100, текущую скорость = 0, а для supercar мы задали улучшенные характеристики при создании экземпляра класса.

Выводы

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

В следующих статьях мы разберем важные методы ООП программирования - геттеры и сеттеры, расскажем что такое public, static, private, protected.

Читайте также: