class A {
public:
A(); // конструктор по умолчанию
};
class B {
public:
explicit B(int x = 0; bool b = true); // конструктор по умолчанию,
}; // см. далее объяснение
// ключевого слова “explicit”
class C {
public:
explicit C(int x); // это не конструктор по
// умолчанию
};Конструкторы классов B и C объявлены в ключевым словом explicit (явный). Это предотвращает их использование для неявных преобразований типов, хотя не запрещает применения, если преобразование указано явно:
void doSomething(B bObject); // функция принимает объект типа B
B bObj1; // объект типа B
doSomething(bObj1); // нормально, B передается doSomething
B bObj(28); // нормально, создает B из целого 28
// (параметр bool по умолчанию true)
doSomething(28); // ошибка! doSomething принимает B,
// а не int, и не существует неявного
// преобразования из int в B
doSomething(B(28)); // нормально, используется конструктор
// B для явного преобразования (приведения)
// int в B (см. в правиле 27 информацию
// о приведении типов)Конструкторы, объявленные как explicit, обычно более предпочтительны, потому что предотвращают выполнение компиляторами неявных преобразований типа (часто нежелательных). Если нет основательной причины для использования конструкторов в неявных преобразованиях типов, я всегда объявляю их explicit. Советую и вам придерживаться того же принципа.
Обратите внимание, что в предшествующем примере приведение выделено. Я и дальше буду использовать такое выделение, чтобы подчеркнуть важность излагаемого материала. (Также я выделяю номера глав, но это только потому, что мне кажется, это выглядит симпатично.)
Конструктор копирования (copy constructor) используется для инициализации объекта значением другого объекта того же самого типа, а копирующий оператор присваивания (copy assignment operator) применяется для копирования значения одного объекта в другой – того же типа:class Widget {
public:
Widget(); // конструктор по умолчанию
Widget(const Widget& rhs); // конструктор копирования
Widget& operator=(const Widget& rhs); // копирующий оператор присваивания
…
};
Widget w1; // вызов конструктора по умолчанию
Widget w2(w1); // вызов конструктора копирования
w1 = w2; // вызов оператора присваивания
// копированиемБудьте внимательны, когда видите конструкцию, похожую на присваивание, потому что синтаксис «=» также может быть использован для вызова конструктора копирования:
Widget w3 = w2; // вызов конструктора копирования!
К счастью, конструктор копирования легко отличить от присваивания. Если новый объект определяется (как w3 в последнем предложении), то должен вызываться конструктор, это не может быть присваивание. Если же никакого нового объекта не создается (как в «w1=w2»), то конструктор не применяется и это – присваивание. Конструктор копирования – особенно важная функция, потому что она определяет, как объект передается по значению. Например, рассмотрим следующий фрагмент:
bool hasAcceptableQuality(Widget w);
…
Widget aWidget;
if (hasAcceptableQuality(aWidget)) …