Еще одна приятная фича C++0x, которой лично мне очень не хватало - делегирующие конструкторы. Дальше я кратко изложу вот этот документ: Delegating Constructors (revision 3)[pdf].
Бывает так, что классу нужно несколько конструкторов, и тогда бывает удобно из одного конструктора вызвать другой. А сделать этого нельзя. Сейчас это решается функциями инициализации. Это несколько неинтуитивный способ и у него есть недостатки.
Пример кода с функцией инициализации.
class X {
void CommonInit();
Y y_;
Z z_;
public:
X();
X( int );
X( W );
};
X::X() : y_(42), z_(3.14) { CommonInit(); }
X::X( int i ) : y_(i), z_(3.14) { CommonInit(); }
X::X( W e ) : y_(53), z_( e ) { CommonInit(); }
Недостатки этого метода:
1. Несмотря на то, что тела у этих конструкторов совпадают, слить их в один не получится.
2. Невозможно делегировать инициализацию переменных.
Начинающие программисты часто не знают, что вызвать один конструтор из другого нельзя и пишут что-то вроде такого:
class X {
int i_;
public:
X();
X( int );
};
X::X() { DoSomethingObservableToThisObject(); }
X::X( int i ) : i_(i) { X(); }
Этот код компилируется, но делает совсем не то, что хотел программист.
Чего будет в С++0x: один конструктор, он называется "делегирующий конструктор", сможет вызывать другой, "целевой конструктор".
Пример кода:
class X {
int i_;
public:
X( int i ) : i_(i) { }
X() : X(42) { } // i_ == 42
};
Сильно лучше, чем было, но я нашла один существенный минус. Если делегирующие конструкторы образуют цикл, то результатом будет undefined behavior. Диагностику новый Стандарт не требует.
Также см. Стандарт C++0x, 12.6.2.
9 коммент.:
спасибо, побольше бы про новый стандарт да на русском :)
2Tor:
спасибо, побольше бы про новый стандарт да на русском :)
Евгений Зуев обещал, что скоро все будет :-).
Почему бы просто не разрешить вызывать конструкторы друг из друга?
2denizzzka:
Почему бы просто не разрешить вызывать конструкторы друг из друга?
Потому что сейчас конструкция вида
X::X( int i ) : i_(i) { X(); }
легальна, она означает создание локального объекта внутри конструктора. И если придать этому выражению другой смысл, то это может сломать чей-нибудь старый код.
Когда нужно сделать что-то общее для нескольких конструкторов, то на ум приходит перекинуть общее в конструктор базового класса. Вызов конструкторов друг из друга усложняет программу.
Например, сколько раз деструктор вызовется, если эксепшен в вызываемом конструкторе. Можно ли будет поймать эксепшен конструктора в другом конструкторе? Будет ли вызван дополнительный конструктор базового класса A(int), если в наследнике пришем B() : B(int) {} ?
2jia3ep
Когда нужно сделать что-то общее для нескольких конструкторов, то на ум приходит перекинуть общее в конструктор базового класса. Вызов конструкторов друг из друга усложняет программу.
У этого решения много минусов.
Допустим, нет базового класса. Вводить еще один класс для этого?
Также не удастся инициализировать переменные класса-наследника.
Например, сколько раз деструктор вызовется, если эксепшен в вызываемом конструкторе. Можно ли будет поймать эксепшен конструктора в другом конструкторе? Будет ли вызван дополнительный конструктор базового класса A(int), если в наследнике пришем B() : B(int) {} ?
A(int) вызовется, если он есть в списке инициализации B(int).
Про исключения не знаю, Стандарт смотреть лень.
Например, сколько раз деструктор вызовется, если эксепшен в вызываемом конструкторе.
Ни разу - но это же проблема для любого недостроенного конструктора.
Будет ли вызван дополнительный конструктор базового класса A(int), если в наследнике пришем B() : B(int) {} ?
Кстати, да. Хороший вопрос.
Наверно можно будет зациклить вызов конструкторов ->
B() : B(int) {}
B(int) : B() {}
Прошу прощения за некропостинг.
На сколько я знаю (хоть и никогда не использовал в рабочем коде), сейчас валиден такой синтаксис:
class A
{
private:
int x_;
int y_;
public:
A():x_(3)
{
}
A(int y):y_(y)
{
this->A::A();
}
};
2Alex:
На сколько я знаю (хоть и никогда не использовал в рабочем коде), сейчас валиден такой синтаксис:
По Стандарту не валиден. Потому что
12.1/1: Constructors do not have names
12.1/2: Because constructors do not have names, they are never found during name
lookup
Т.е. даже компиляться не должно. Однако VC++2008 у меня это скомпилял без проблем. Возможно даже это будет работать как ожидалось. Но использовать такой код я бы не стала...
Еще подробное обсуждение подобного синтаксиса есть здесь.
Отправить комментарий