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

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

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

В прошлой теме был разработан следующий класс:

#include class Person < public: std::string name; unsigned age; void print() < std::cout >; int main() < Person person; // вызов конструктора person.name = "Tom"; person.age = 22; person.print(); >

Здесь при создании объекта класса Person, который называется person

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

Теперь определим свой конструктор. Например, в примере выше мы устанавливаем значения для полей класса Person. Но, допустим, мы хотим, чтобы при создании объекта эти поля уже имели некоторые значения по умолчанию. Для этой цели определим конструктор:

#include class Person < public: std::string name; unsigned age; void print() < std::cout Person(std::string p_name, unsigned p_age) < name = p_name; age = p_age; std::cout >; int main() < Person tom("Tom", 38); // создаем объект - вызываем конструктор tom.print(); >

Теперь в классе Person определен конструктор:

Person(std::string p_name, unsigned p_age)

По сути конструктор представляет функцию, которая может принимать параметры и которая должна называться по имени класса. В данном случае конструктор принимает два параметра и передает их значения полям name и age, а затем выводит сообщение о создании объекта.

Если мы определяем свой конструктор, то компилятор больше не создает конструктор по умолчанию. И при создании объекта нам надо обязательно вызвать определенный нами конструктор.

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

После этого вызова у объекта person для поля name будет определено значение «Tom», а для поля age — значение 38. Вполедствии мы также сможем обращаться к этим полям и переустанавливать их значения.

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

Тажке можно присвоить объекту результат вызова конструктора:

По сути она будет эквивалетна предыдущей.

Консольный вывод определенной выше программы:

Person has been created Name: Tom Age: 38

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

#include class Person < public: std::string name; unsigned age; void print() < std::cout Person(std::string p_name, unsigned p_age) < name = p_name; age = p_age; std::cout >; int main() < Person tom; Person bob; Person sam; tom.print(); bob.print(); sam.print(); >

Здесь создаем три разных объекта класса Person (условно трех разных людей), и соответственно в данном случае консольный вывод будет следующим:

Person has been created Person has been created Person has been created Name: Tom Age: 38 Name: Bob Age: 42 Name: Sam Age: 25

Определение нескольких конструкторов

Подобным образом мы можем определить несколько конструкторов и затем их использовать:

#include class Person < std::string name<>; unsigned age<>; public: void print() < std::cout Person(std::string p_name, unsigned p_age) < name = p_name; age = p_age; >Person(std::string p_name) < name = p_name; age = 18; >Person() < name = "Undefined"; age = 18; >>; int main() < Person tom; // вызываем конструктор Person(std::string p_name, unsigned p_age) Person bob; // вызываем конструктор Person(std::string p_name) Person sam; // вызываем конструктор Person() tom.print(); bob.print(); sam.print(); >

В классе Person определено три конструктора, и в функции все эти конструкторы используются для создания объектов:

Name: Tom Age: 38 Name: Bob Age: 18 Name: Undefined Age: 18

Хотя пример выше прекрасно работает, однако мы можем заметить, что все три конструктора выполняют фактически одни и те же действия — устанавливают значения переменных name и age. И в C++ можем сократить их определения, вызова из одного конструктора другой и тем самым уменьшить объем кода:

#include class Person < std::string name<>; unsigned age<>; public: void print() < std::cout Person(std::string p_name, unsigned p_age) < name = p_name; age = p_age; std::cout Person(std::string p_name): Person(p_name, 18) // вызов первого конструктора < std::cout Person(): Person(std::string("Undefined")) // вызов второго конструктора < std::cout >; int main() < Person sam; // вызываем конструктор Person() sam.print(); >

Запись Person(string p_name): Person(p_name, 18) представляет вызов конструктора, которому передается значение параметра p_name и число 18. То есть второй конструктор делегирует действия по инициализации переменных первому конструктору. При этом второй конструктор может дополнительно определять какие-то свои действия.

Читайте также:  Java get file info

Таким образом, следующее создание объекта

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

Данная техника еще называется делегированием конструктора , поскольку мы делегируем инициализацию другому конструктору.

Параметры по умолчанию

Как и другие функции, конструкторы могут иметь параметры по умолчанию:

#include class Person < std::string name; unsigned age; public: // передаем значения по умолчанию Person(std::string p_name = "Undefined", unsigned p_age = 18) < name = p_name; age = p_age; >void print() < std::cout >; int main() < Person tom; Person bob; Person sam; tom.print(); // Name: Tom Age: 38 bob.print(); // Name: Bob Age: 18 sam.print(); // Name: Undefined Age: 18 >

Инициализация констант и списки инициализации

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

class Person < const std::string name; unsigned age<>; public: void print() < std::cout Person(std::string p_name, unsigned p_age) < name = p_name; age = p_age; >>;

Этот класс не будет компилироваться из-за отсутствия инициализации константы name. Хотя ее значение устанавливается в конструкторе, но к моменту, когда инструкции из тела конструктора начнут выполняться, константы уже должны быть инициализированы. И для этого необходимо использовать списки инициализации :

#include class Person < const std::string name; unsigned age<>; public: void print() < std::cout Person(std::string p_name, unsigned p_age) : name  < age = p_age; >>; int main() < Person tom; tom.print(); // Name: Tom Age: 38 >

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

Person(std::string p_name, unsigned p_age) : name

Здесь выражение name позволяет инициализировать константу значением параметра p_name. Здесь значение помещается в фигурные скобки, но также можно использовать кргулые:

Person(std::string p_name, unsigned p_age) : name(p_name)

Списки инициализации пободным образом можно использовать и для присвоения значений переменным:

class Person < const std::string name; unsigned age; public: void print() < std::cout Person(std::string p_name, unsigned p_age) : name(p_name), age(p_age) < >>;

При использовании списков инициализации важно учитывать, что передача значений должна идти в том порядке, в котором константы и переменные определены в классе. То есть в данном случае в классе сначала определена константа name, а потом переменная age. Соответственно в таком же порядке идет передача им значений. Поэтому при добавлении дополнительных полей или изменения порядка существующих придется следить, чтобы все инициализировалось в належащем порядке.

Читайте также:  Java io serializable to string

Источник

Конструкторы и деструкторы

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

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

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(); // конструктор по умолчанию
>;

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

Читайте также:  Javascript alert box example-1

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

  • формальный параметр – объект, передаваемый по значению, создается в стеке в момент вызова функции и инициализируется копией фактического параметра;
  • результат функции – объект, передаваемый по значению, в момент выполнения оператора 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 для каждого члена vect будет вызываться индивидуальный деструктор. Результат работы программы

Комментариев к записи: 2

Источник

Оцените статью