Classes and class hierarchies
C.40
Определите конструктор, если класс имеет инвариант
Причина
Для этого нужны конструкторы.
Пример
class Date { // Date представляет корректную дату
// в диапазоне от 1 января 1900 года до 31 декабря 2100 года
Date(int dd, int mm, int yy)
:d{dd}, m{mm}, y{yy}
{
if (!is_valid(d, m, y)) throw Bad_date{}; // обеспечьте инвариант
}
// ...
private:
int d, m, y;
};
Часто хорошей идеей является выразить инвариант как Ensures на конструкторе.
Примечание
Конструктор может использоваться для удобства, даже если класс не имеет инварианта. Например:
struct Rec {
string s;
int i {0};
Rec(const string& ss) : s{ss} {}
Rec(int ii) :i{ii} {}
};
Rec r1 {7};
Rec r2 {"Foo bar"};
Примечание
Правило списка инициализаторов C++11 устраняет необходимость во многих конструкторах. Например:
struct Rec2{
string s;
int i;
Rec2(const string& ss, int ii = 0) :s{ss}, i{ii} {} // избыточно
};
Rec2 r1 {"Foo", 7};
Rec2 r2 {"Bar"};
Конструктор Rec2 избыточен. Также, значение по умолчанию для int было бы лучше сделать как инициализатор члена по умолчанию.
См. также: построить корректный объект и конструктор выбрасывает.
Применение
- Отмечайте классы с определёнными пользователем операциями копирования, но без конструктора (определённое пользователем копирование - это хороший указатель на то, что класс имеет инвариант)