Вы получили жертву так называемой нарезки объектов ; Я немного упростил код для лучшей иллюстрации (не имеет значения, если вы получаете указатель через умный указатель или необработанный указатель ...)
class Scaler
{
Test child;
Scaler(Test* child, double maximum) : child(*child) { }
// here!!!
};
Что именно происходит, так это то, что вы назначаете производный класс экземпляру базового класса. Базовый класс (как значение) не может содержать экземпляр производного класса, поэтому все данные, принадлежащие производному классу, «обрезаются», или, другими словами, только часть базового класса производного класса копируется в член теста , Поскольку теперь это истинный экземпляр Test
, он будет вызывать Test::run()
, который просто возвращает Score()
, и вы в результате делитесь на 0 ...
Так что теперь, как вы уже ввели умные указатели, тогда прибыль от:
class Scaler
{
std::shared_ptr<Test> child;
Scaler(std::shared_ptr<Test> const& child, double maximum)
// you don't need the reference counting stuff for the parameter,
// so you can pass as reference
: child(child) // assigning the reference to our member
// (now with reference count management)
{ }
};
Более простой вариант использует необработанные ссылки:
class Scaler
{
Test& child;
// ^ (!)
Scaler(Test& child, double maximum)
: child(child), maximum(maximum)
{ }
};
Это даже позволяет использовать более простой код в ваших тестовых функциях:
DummyTest test(Score 1,1));
Scaler s(test, 5);
Имейте в виду, однако, что с необработанными ссылками вы должны убедиться, что ссылочный тест живет, по крайней мере, столько, сколько ссылающийся тест (или, по крайней мере, пока ссылочный тест все еще использует свою ссылку), в противном случае Вы страдаете от неопределенного поведения. Это гарантируется приведенным выше фрагментом, но, если вы сделаете это так:
Scaler s(DummyTest(Score 1,1));
s.run();
Теперь, после возврата из конструктора, экземпляр DummyTest
снова исчезает, и у вас есть свисающая ссылка в s.run()
.