При написании класса с несколькими конструкторами, необходимость указывать значения по умолчанию всем членам в каждом конструкторе приведет к написанию лишнего кода. Если вы обновите значение по умолчанию какого-то одного члена, то вам придется лезть в каждый конструктор.
Начиная с C++11, обычным переменным-членам класса (те, которые не используют ключевое слово static) можно задать значение по умолчанию напрямую — использовать инициализацию:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> class Something { private: double m_length = 3.5; // m_length имеет значение по умолчанию 3.5 double m_width = 3.5; // m_width имеет значение по умолчанию 3.5 public: Something() { // Этот конструктор использует значения по умолчанию, приведенные выше, так как здесь эти значения не переопределяются } void print() { std::cout << "length: " << m_length << " and width: " << m_width << '\n'; } }; int main() { Something a; // a.m_length = 3.5, a.m_width = 3.5 a.print(); return 0; } |
Результат выполнения программы:
length: 3.5 and width: 3.5
Таким образом, вы предоставляете значения по умолчанию вашим переменным-членам, которые будут использоваться вашими конструкторами, если сами конструкторы не предоставят эти значения (через списки инициализации членов).
Однако конструкторы все еще определяют тип объектов, которые могут быть созданы, например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include <iostream> class Something { private: double m_length = 3.5; double m_width = 3.5; public: // Обратите внимание, здесь нет конструктора по умолчанию Something(double length, double width) : m_length(length), m_width(width) { // m_length и m_width инициализируются этим конструктором (значения по умолчанию, приведенные выше, не используются) } void print() { std::cout << "length: " << m_length << ", width: " << m_width << '\n'; } }; int main() { Something a; // не скомпилируется, так как требуется конструктор по умолчанию, даже если члены класса имеют значения по умолчанию return 0; } |
Несмотря на то, что мы предоставили значения по умолчанию всем переменным-членам класса, конструктор по умолчанию предоставлен не был, поэтому мы не можем создать объект класса Something без параметров.
Если предоставлено значение по умолчанию, и конструктор инициализирует член через список инициализации членов, то приоритет будет у списка инициализации членов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <iostream> class Something { private: double m_length = 3.5; double m_width = 3.5; public: Something(double length, double width) : m_length(length), m_width(width) { // m_length и m_width инициализируются конструктором (значения по умолчанию, приведенные выше, не используются) } void print() { std::cout << "length: " << m_length << " and width: " << m_width << '\n'; } }; int main() { Something a(4.5, 5.5); a.print(); return 0; } |
Результат выполнения программы:
length: 4.5 and width: 5.5
Правило: Используйте инициализацию нестатических членов для указания значений по умолчанию переменным-членам.
Тест
Задание №1
Добавьте в следующую программу инициализацию нестатических членов и список инициализации членов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#include <string> #include <iostream> class Thing { private: std::string m_color; double m_radius; public: // Конструктор по умолчанию без параметров Thing() { m_color = "blue"; m_radius = 20.0; } // Конструктор с параметром color (для radius предоставлено значение по умолчанию) Thing(const std::string &color) { m_color = color; m_radius = 20.0; } // Конструктор с параметром radius (для color предоставлено значение по умолчанию) Thing(double radius) { m_color = "blue"; m_radius = radius; } // Конструктор с параметрами color и radius Thing(const std::string &color, double radius) { m_color = color; m_radius = radius; } void print() { std::cout << "color: " << m_color << " and radius: " << m_radius << '\n'; } }; int main() { Thing defl; defl.print(); Thing red("red"); red.print(); Thing thirty(30.0); thirty.print(); Thing redThirty("red", 30.0); redThirty.print(); return 0; } |
Результат выполнения программы должен быть следующим:
color: blue and radius: 20
color: red and radius: 20
color: blue and radius: 30
color: red and radius: 30
Ответ №1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#include <iostream> #include <string> class Thing { private: std::string m_color = "blue"; double m_radius = 20.0; public: // Конструктор по умолчанию без параметров (color и radius используют значения по умолчанию) Thing() { } // Конструктор с параметром color (для radius предоставлено значение по умолчанию) Thing(const std::string &color): m_color(color) { } // Конструктор с параметром radius (для color предоставлено значение по умолчанию) Thing(double radius): m_radius(radius) { } // Конструктор с параметрами color и radius Thing(const std::string &color, double radius): m_color(color), m_radius(radius) { } void print() { std::cout << "color: " << m_color << " and radius: " << m_radius << '\n'; } }; int main() { Thing defl; defl.print(); Thing red("red"); red.print(); Thing thirty(30.0); thirty.print(); Thing redThirty("red", 30.0); redThirty.print(); return 0; } |
Задание №2
Зачем мы объявили пустой конструктор по умолчанию в программе из задания №1? Все же переменные-члены и так имеют значения по умолчанию.
Ответ №2
Объект defl
класса Thing будет искать конструктор по умолчанию. Если его не будет, то компилятор выдаст ошибку.
Вроде сделал, смотрю — по-умолчанию то нету инициализации, а потом смотрю а записал то иначе.
Упустил, ведь это вызывает загромождение.
Тут ладно, в большой программе — нет.
Но все равно, успехи на лицо. Уверенно и лаконично.
Спасибо за курс!
По-моему в вашей реализации чтобы поменять в выводе например blue на green надо конструкторы править в двух местах, а у автора в одном. В сокращении кол-ва правок и есть смысл задания.
Либо я чего-то не понимаю либо в вашем коде в строке
const std::string &color абсолютно бесполезно ставить конст. Для чего он нужен? Если бы в привате было всё константными значениями тогда список инициализации зашёл бы хорошо. Но я голову ломаю в смысле написания константного аргумента если если он будет буквально константным на время жизни в две строки. Суть в том чтоб обьяснить смысл список инициализации так смысл задания был не нагружать безсмысленным кодом прогу а облегчить её
Спасибо! Долгие лета!
И тебе друг!
С инициализацией нестатических членов получается нужно аж 4 конструктора (в тесте выше). При этом через список инициализации или присваивания достаточно всего 2 конструктора (в тесте урока 116). Учитывая, что рекомендуется использовать меньшее кол-во конструкторов, выходит что инициализация списком лучше?
Если вы обновите значение по умолчанию какого-то одного члена, то вам придется лезть в каждый конструктор.
Вот этот урок пока что не дается. Значения по умолчанию которые ни где не используются, но нужны потому что "Объект defl класса Thing будет искать конструктор по умолчанию. Если его не будет, то компилятор выдаст ошибку."
Скопировал полностью код с задания номер 1. Намеряно не выполнил задание номер 1. Компилятор не ругается, программа выполняется. 🙂
Может быть свежие компиляторы научились боротся с этой ошибкой…
СPP v.11, QT 5.12, MinGW(64bit)
Так в задании код корректный. Его нужно "доработать напильником" в другой стиль.
Пустой конструктор нужен просто как инструкция компилятору. Без пустого конструктора попытка создать объект без параметров невозможна. При этом сам конструктор вовсе не обязательно что-то должен делать.
Такие ситуации случаются и без танцев с инициализацией. Например, обычно нужно создавать объекты не беспокоясь о начальных значениях, но все-таки создан конструктор с возможностью задать некоторые значения. Как только мы создаем такой конструктор, создавать объекты без параметров становится невозможно. Так как нас начальные значения не беспокоят, то нужно создать пустой конструктор, который ничего не делает, но показывает компилятору, что "ничего не делать — это фича, а не баг"
А когда будет template? Сколько всего уроков планируется?
Для Шаблонов есть отдельная глава. Но еще до той главы уроков 50. Всего еще уроков около 90 осталось перевести.
С нетерпением ждем новых уроков!!!!!!!
Это, наверное, лучший курс по обучению программированию, что мне встречался в сети! Автору респект!
Спасибо, буду продолжать 🙂