Ето една проста С++(98) конструкция, която не рядко се бърка. Хора ползващи Java или друг език в миналото си, и С++ в настоящето си се сблъскват с това :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Foo { public: Foo(){ bar = 42; printf("Foo()\n"); } Foo(int) { buzz = 196; Foo(); printf("Foo(int); %i %i", bar, buzz); } private: int bar; int buzz; }; int main() { Foo foo(411); } |
На пръв поглед това не трябва да се компилира. Имаме контруктор, който вика друг конструктор (и ползваме C++ < 11). А това не е разрешено в тази версия на езика (макар не всички да го знаят). За да е тотално объркването обаче, всеки компилатор успява да го компилира без грешки. Този код работи и не нарушава стандарта. Не прави обаче съвсем това, което човек би очаквал.
1 |
printf("Foo(int); %i %i", bar, buzz); //Foo(int); <undefined> 196 |
Причината за това е, че Foo() не вика конструктора за обекта, който се конструира във Foo(int), а създава temporary object (без име) !
За по ясно, вместо Foo(); там може да пише int(); … така ще сме създали един int на стека, който както и Foo(); ще спре да съществува след като излезем от scope-а на Foo(int). Може да стане по-ясно, ако добавим деструктор, в който има само printf.
1 2 3 |
~Foo() { printf("~Foo()\n"); } |
Можем обаче да накараме нещата да сработят .. Ако ползваме placement new, бихме могли да извикаме конструктура върху обекта, който очакваме. Ето така :
1 2 3 4 5 |
Foo(int) { buzz = 196; new (this) Foo(); //here be dragons printf("Foo(int); %i %i", bar, buzz); } |
Показаното по-горе работи (поне в gcc, clang && cl). Но не е съвсем гарантирано, че ще работи навсякъде и винаги. Добрият стар init() метод е по-подходящ тогава.