Было бы удобно производить инициализацию переменных класса Distance в момент их создания, то есть использовать объявления типа
Distance width(5, 6.25):
где определяется объект width, сразу же инициализируемый значениями 5 и 6.25 для футов и дюймов соответственно.
Чтобы сделать это, вызовем конструктор следующим образом:
Distance(int ft, float in): feet(ft). inches(in)
{ }
Мы инициализируем поля feet и inches теми значениями, которые передаются конструктору в качестве аргументов.
Тем не менее, мы хотим сохранить возможность определять переменные класса Distance без инициализации, подобно тому, как мы делали в программе ENGL0BJ.
Distance distl. dist2:
В программе ENGL0BJ не было конструктора, но определения работали без ошибок. Почему же они работали без конструктора? Это объясняется тем, что компилятор автоматически встраивает в программу конструктор без параметров, который и создает переменные класса, несмотря на то, что явного определения конструктора мы не делали. Такой конструктор без параметров называется конструктором по умолчанию. Если бы конструктор по умолчанию не создавался автоматически, то мы не смогли бы определять переменные классов, в которых отсутствует конструктор.
|
|
Зачастую нам хотелось бы, чтобы начальные значения полям объекта присваивались также и в конструкторе без параметров. Если возложить эту функцию на конструктор по умолчанию, то мы не сможем узнать, какими значениями были инициализированы поля. Если же для нас важно, какими значениями будут инициализироваться поля объекта класса, то нам следует явно определить конструктор. В программе ENGLC0N мы поступаем подобным образом:
Distance(): feet(0). inches(0.0) //конструктор по умолчанию
{ } //тело функции пусто, никаких действий не производится
Члены класса инициализируются константными значениями, в данном случае целым значением 0 для переменной feet и вещественным значением 0.0 для переменной inches. Значит, мы можем использовать объекты, инициализируемые с помощью конструктора без параметров, будучи уверенными в том, что поля объекта имеют нулевые, а не другие, случайные, значения.
Теперь у нас имеется два явно определенных конструктора с одним и тем же именем Distance(), и поэтому говорят, что конструктор является перегруженным. Какой из этих двух конструкторов исполняется во время создания нового объекта, зависит от того, сколько аргументов используется при вызове:
Distance length: //вызывает первый конструктор
Distance width(11, 6.0): //вызывает второй конструктор
Определение методов класса вне класса
До сих пор мы всегда определяли методы класса внутри самого класса. На самом деле это не является обязательным. В примере ENGLC0N метод add_dist() определен вне класса DistanceQ. Внутри определения класса содержится лишь прототип функции add_dist():
|
|
void add_dist(Distance, Distance):
Такая форма означает, что функция является методом класса, однако ее определение следует искать не внутри определения класса, а где-то в другом месте листинга.
В примере ENGLC0N функция add_dist() определена позже, чем класс Distance(). Ее код взят из программы ENGLSTRC:
void Distance::add_dist(Distance d2, Distance d3) {
inches = d2.inches + d3.inches; // сложение дюймов
feet =0; //с возможным заемом
if(inches >= 12.0) // если число дюймов больше 12.0.
{ //то уменьшаем число дюймов
inches -= 12.0; // на 12.0 и увеличиваем
feet++; // число футов на 1
}
feet +=d2.feet + d3.feet: // сложение футов
}
Заголовок функции содержит не встречавшиеся нам ранее синтаксические элементы. Перед именем функции add_dist() стоит имя класса Distance и новый символ::. Этот символ является знаком операции глобального разрешения. Такая форма записи устанавливает взаимосвязь функции и класса, к которой относится эта функция. В данном случае запись Distance::add_list() означает, что функция add_dist() является методом класса Distance.