#c #constructor
#c #конструктор
Вопрос:
Я программирую на C десятилетиями и никогда не ожидал такого ограничения.
Есть ли что-то очевидное, что я упускаю из виду? Я думаю, мне нужно заставить X()
конструктор принимать аргумент для i и позволить этому конструктору инициализировать его? В чем может быть смысл этого странного ограничения? Я могу понять запрет на инициализацию объекта дважды, но он вообще не инициализируется! (Или, я полагаю, в отсутствие явного конструктора, конструктор X предоставит ему инициализацию по умолчанию?) И все же:
class X {
public:
X() {};
int i;
};
class X1 : public X {
public:
X1() : i(1){};
};
приводит к ошибке:
initial.cpp: In constructor 'X1::X1()':
initial.cpp:11:10: error: class 'X1' does not have any field named 'i'
X1() : i(1){};
^
Комментарии:
1. Инициализация
X
таких членов, какi
принадлежитX
. В противном случае вы потенциально можете инициализировать элемент несколько раз. Компилятор не может определить, действительно ли он будет инициализирован несколько раз или нет. И если решение состоит в том, чтобы просто предоставитьX
конструктор, я сомневаюсь, что кто-нибудь потрудится изучить возможность его изменения.2. Построение an
X1
всегда вызывает конструкторX
, независимо от того, закодирован он явно или нет. В этом случае конструктор по умолчаниюX
(который не принимает никаких аргументов) неявно вызывается конструктором по умолчаниюX1
, и он (в коде yoru) инициализируется по умолчаниюX::i
. Этот конструктор выполняет роль инициализацииX
членов s. Итак, еслиX1
бы конструктору s было каким-то образом разрешено инициализироватьX::i
напрямую, результатом логически была бы инициализацияX::i
дважды.3. @Peter Но каковы недостатки, если мы инициализируем X::i дважды? В некоторых случаях все в порядке
4. @WoooHaaaa это аннулировало бы всю идею «порядка инициализации» и все, что полагается на это в определениях языка. В этом случае это ничего не дает, но что, если «i» — это сложный объект, который создает другие объекты, имеет побочные эффекты, права собственности и т. Д.? «Некоторые случаи» превращаются в целую повестку об исключениях в пределах 300 страниц языковых правил, если это разрешено делать описанным выше способом. Если вас не волнуют затраты, просто сделайте
X1() { i = 1; };
. Идеологически неверно, но разрешено, еслиi
является общедоступным или защищенным.5. @WoooHaaaa — В некоторых случаях это может быть нормально. Существует множество случаев, когда этого не происходит, например, когда инициализация
X::i
имеет побочные эффекты. В любом случае семантически каждый объект (включая элемент объекта) инициализируется только один раз. Изменение этого, чтобы разрешить конкретные случаи, когда множественная инициализация не имеет значения, значительно усложнило бы правила, связанные с порядком инициализации, без особых преимуществ.
Ответ №1:
Это означает, что поскольку инициализация объекта является частью создания этих объектов, вам либо нужно обеспечить инициализацию через X, либо перепроектировать, чтобы иметь возможность инициализировать его напрямую:
class X {
public:
X() {};
X(int _i) : i(_i) {};
int i;
};
или
class X {
public:
int i;
};
итак, вы можете сделать это:
class X1 : public X {
public:
X1() : X{1}{};
};
Разрешение того, что вы пытались сделать, означало бы, что мы должны были вставить новое поведение инициализации в существующую последовательность, которая может быть скомпилирована уже как часть другого модуля компиляции или разрешить инициализацию этого элемента дважды. Первое проблематично реализовать с использованием всей концепции реализации C в качестве собственных компиляторов, последнее делает последовательность инициализации нелинейной и противоречит существующей идеологии.
Обратите внимание, что обычно композиция предпочтительнее наследования, если вы нацелены на область хранения, а не на поведение, т.е.
InterfaceX {
// declarations to be used in descendants
};
class X1 : public InterfaceX
{
X m_x;
public:
X1() : m_x{1} {};
};
Если нам действительно требуется полиморфное использование, мы бы использовали InterfaceX
для виртуальных методов. И если нам нужно будет «сократить» X1 до X, должен быть оператор преобразования operator X () {return m_x; }