Как вызвать конструктор c
Конструкторы представляют специальную функцию, которая имеет то же имя, что и класс, которая не возвращает никакого значения и которая позволяют инициалилизировать объект класса во время го создания и таким образом гарантировать, что поля класса будут иметь определенные значения. При каждом создании нового объекта класса вызывается конструктор класса.
В прошлой теме был разработан следующий класс:
#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 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 tom("Tom", 38);
После этого вызова у объекта person для поля name будет определено значение «Tom», а для поля age — значение 38. Вполедствии мы также сможем обращаться к этим полям и переустанавливать их значения.
В качестве альтернативы для создания объекта можно использовать инициализатор в фигурных скобках:
Person tom;
Тажке можно присвоить объекту результат вызова конструктора:
Person tom = Person("Tom", 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. То есть второй конструктор делегирует действия по инициализации переменных первому конструктору. При этом второй конструктор может дополнительно определять какие-то свои действия.
Таким образом, следующее создание объекта
Person sam;
будет использовать третий конструктор, который в свою очередь вызывает второй конструктор, а тот обращается к первому конструктору.
Данная техника еще называется делегированием конструктора , поскольку мы делегируем инициализацию другому конструктору.
Параметры по умолчанию
Как и другие функции, конструкторы могут иметь параметры по умолчанию:
#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. Соответственно в таком же порядке идет передача им значений. Поэтому при добавлении дополнительных полей или изменения порядка существующих придется следить, чтобы все инициализировалось в належащем порядке.
Как из одного конструктора вызвать другой конструктор в C++?
При этом одновременно с вызовом другого конструктора нельзя вызывать конструкторы членов класса, например:
struct B < B() : x(1) <>B(int x_) : B(), x(x_) <> // ОШИБКА: нельзя одновременно вызвать B() и x() int x; >;
Как только был вызван один из конструкторов, объект считается полностью сконструированным, по этому исключение из вызывающего конструктора приведет к вызову деструктора класса:
struct C < C(const char*) < throw 1; >C() <> C(int) : C() < throw 1; >~C() < std::cout >; int main() < try < C c(""); // ничего не будет напечатано, деструктор не будет вызван >catch (. ) <> try < C c(1); // будет напечатано "~C" >catch (. ) <> >
Конструкторы и наследование
В иерархии классов допускается, чтобы у базовых и производных классов были свои собственные конструкторы. В связи с этим возникает следующий резонный вопрос: какой конструктор отвечает за построение объекта производного класса: конструктор базового класса, конструктор производного класса или же оба? На этот вопрос можно ответить так: конструктор базового класса конструирует базовую часть объекта, а конструктор производного класса — производную часть этого объекта. И в этом есть своя логика, поскольку базовому классу неизвестны и недоступны любые элементы производного класса, а значит, их конструирование должно происходить раздельно.
Если конструктор определен только в производном классе, то все происходит очень просто: конструируется объект производного класса, а базовая часть объекта автоматически собирается его конструктором, используемым по умолчанию.
Когда конструкторы определяются как в базовом, так и в производном классе, процесс построения объекта несколько усложняется, поскольку должны выполняться конструкторы обоих классов. В данном случае приходится обращаться к ключевому слову base, которое находит двоякое применение: во-первых, для вызова конструктора базового класса; и во-вторых, для доступа к члену базового класса, скрывающегося за членом производного класса.
С помощью формы расширенного объявления конструктора производного класса и ключевого слова base в производном классе может быть вызван конструктор, определенный в его базовом классе. Ниже приведена общая форма этого расширенного объявления:
конструктор_производного_класса(список_параметров) : base (список_аргументов) < // тело конструктора >
где список_аргументов обозначает любые аргументы, необходимые конструктору в базовом классе. Обратите внимание на местоположение двоеточия.
Давайте рассмотрим пример:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 < class MyClass < public int x, y, z; // Конструктор базового класса public MyClass(int x, int y, int z) < this.x = x; this.y = y; this.z = z; >> class ClassA : MyClass < int point; // Конструктор производного класса public ClassA(int point, int x, int y, int z) : base(x, y, z) < this.point = point; >public void Pointer(ClassA obj) < obj.x *= obj.point; obj.y *= obj.point; obj.z *= obj.point; Console.WriteLine("Новые координаты объекта: ", obj.x, obj.y, obj.z); > > class Program < static void Main() < ClassA obj = new ClassA(10, 1, 4, 3); Console.WriteLine("Координаты объекта: ", obj.x, obj.y, obj.z); obj.Pointer(obj); Console.ReadLine(); > > >
С помощью ключевого слова base можно вызвать конструктор любой формы, определяемой в базовом классе, причем выполняться будет лишь тот конструктор, параметры которого соответствуют переданным аргументам.
А теперь рассмотрим вкратце основные принципы действия ключевого слова base. Когда в производном классе указывается ключевое слово base, вызывается конструктор из его непосредственного базового класса. Следовательно, ключевое слово base всегда обращается к базовому классу, стоящему в иерархии непосредственно над вызывающим классом. Это справедливо даже для многоуровневой иерархии классов. Аргументы передаются базовому конструктору в качестве аргументов метода base(). Если же ключевое слово отсутствует, то автоматически вызывается конструктор, используемый в базовом классе по умолчанию.
Как вызвать конструктор с параметром, для класса являющегося членом другого класса.
Есть два класса c1 и c2 У каждого класса конструктор с параметром. Класс c1 является членом класса c2.
class c1< int v; public: c1(int n);// конструктор класса c1 >; c1::c1(int n) < v=n; printf("Constructior c1.\n"); >class c2< int v; public: c2(int n); // конструктор класса c2 c1 e1; >; c2::c2(int n) < v=n; e1.c1(n); //пытаюсь вызвать конструктор класса c1, но не выходит printf("Constructior c2\n"); >int main(int argc,char **argv)
При компиляции выдается сообщение о ошибке
test.cpp: In constructor ‘c2::c2(int)’: test.cpp:29: error: no matching function for call to ‘c1::c1()’ test.cpp:17: note: candidates are: c1::c1(int) test.cpp:12: note: c1::c1(const c1&) test.cpp:31: error: invalid use of ‘class c1’
Я так понимаю, что ошибка из за того, что конструктор вложенного класса имеет параметр. Вопрос. Как передать параметр конструктору члена класса?
Mixaluch384
04.11.11 23:08:48 MSK