Вы не вызываете конструктор A напрямую, конструктор по умолчанию B делает это за вас. Если бы это работало по-другому, вы никогда не смогли бы создать какой-либо класс, который наследует что-либо в частном порядке.
Это связано с тем, что B не имеет конструктора копирования для типа A. Единственный конструктор, который может применяться здесь, — это (по умолчанию) конструктор копирования типа B, который принимает B в качестве аргумента.
Конструкторы привязаны к своему классу, они не наследуют в том же смысле, что и функции. Как объяснить... Основная цель состоит в том, чтобы каждый класс всегда конструировал полностью. Таким образом, чтобы построить A любого типа (будь то автономный или как часть B), должен быть запущен конструктор A. Точно так же, чтобы создать объект класса B, должен быть запущен конструктор класса B. Если вы «наследуете» конструктор A::A(), вы сможете создавать объекты B, которые не были построены полностью. В этом случае конструктор A::A() не выполнил ни одной части последовательности построения для B, оставив B в недопустимом состоянии.
Давайте попробуем другой источник. Мы оставляем A как есть, а B меняем следующим образом:
struct B : private A {
B () { val = 42; }
void foo () { if (val != 42) abort (); }
using A::A;
int val;
};
Теперь предположим, что мы, э-э, получаем букву Б, не создавая ее:
B b (a); // illegal, but for the sake of argument.
b.foo ();
Мы указали, что мы создаем этот B с помощью конструктора A::A, поэтому единственный код, который будет выполнен, — это A::A(). В частности, B::B() не выполняется, поэтому val будет иметь любое значение, которое в данный момент было в стеке. Вероятность того, что это будет 42, равна 1 к 2^32, другими словами, маловероятна.
Что произойдет при вызове B.foo()? Объект не находится в допустимом состоянии (val не равен 42), поэтому приложение прерывается. Ой!
Это, конечно, надуманный пример, но он показывает, что использование несконструированного объекта — это очень плохо, и поэтому язык не позволяет вам создавать любые такие объекты.
- Они не являются неиспользованными. В конструкторах происходят всевозможные действия (запись в cout), которые нельзя просто устранить.
Этот шаблон проектирования много раз встречается в C++, например, в std::mutex. Простого объявления блокировки достаточно для блокировки и разблокировки, но нет необходимости ссылаться на блокировку после объявления. Поскольку это распространенный шаблон проектирования, предупреждение было бы неуместным.
Только если компилятор сможет доказать, что создание и уничтожение локальной переменной не имеет побочных эффектов, и обнаружит, что она не используется, вы можете увидеть предупреждение (в зависимости от компилятора).
person
H. Guijt
schedule
31.07.2016