Вот код, который вызывает проблему:
class Base
{
public:
virtual void fun()
{
cout<<"Base";
}
};
class Der:public Base
{
Base &pb;
public:
Der(Base&b):pb(b){}
virtual void fun()
{
cout<<"Der...";
pb.fun();
}
};
int main()
{
Der(Der(Base())).fun();
return 0;
}
Запустите этот код, и в результате появится «Der … Base …»! Это так удивительно, я не могу понять, почему результат не «Der … Der … Base», что логически правильно ?!
Затем я заменяю член в классе Der Base&pb
с Base*pb
и измените код на легальный, в конце концов, вывод будет правильным: «Der … Der … Base»!
Я отлаживаю код и нахожу, что когда я использую Base&pb
, конструктор Der запускается только один раз при использовании Base*pb
, конструктор бежал дважды правильно!
Любой, кто может объяснить мне, что случилось и почему?
В Der(Der(Base())).fun()
выражение внутреннего Der(Base())
дает rvalue
— компилятор оптимизирует код с помощью копия elision и удаляет ненужное копирование объектов.
В дополнение к ответу @ icepack и последующему обсуждению в комментариях (резюме: код Der(der)
является приведением, которое может быть или не быть реализовано с использованием конструктора; в вашем случае это не так), обходной путь для вас: вы должны четко заявить о своем намерении, не используя конструктор.
Я бы переписал ваш код примерно так:
class Base
{
public:
virtual void fun()
{
cout<<"Base";
}
};
class Der:public Base
{
Base &pb;
Der(Base& b) : pb(b) {}
public:
static Der Decorate(Base&& b){ return Der(b); }
virtual void fun()
{
cout<<"Der...";
pb.fun();
}
};
int main()
{
Der::Decorate(Der::Decorate(Base())).fun();
return 0;
}
Изменение кода для принятия указателя это просто:
class Base
{
public:
virtual void fun()
{
cout << "Base";
}
};
class Der : public Base
{
Base* pb;
Der(Base* b) : pb(b) {}
public:
static Der Decorate(Base* b){ return Der(b); }
virtual void fun()
{
cout << "Der...";
pb->fun();
}
};
int main()
{
Der::Decorate(&Der::Decorate(&Base())).fun();
return 0;
}