почему этот код возвращает -nan (ind)? c ++ - PullRequest
0 голосов
/ 25 августа 2018

Я пытаюсь написать класс Scaler, который проходит дочерний тест с оценкой.

Scaler использует метод шкалы своего дочернего класса для масштабирования своего значения (например, 1/1 со шкалой 5 = 5/5 или 0/1 со шкалой 5 = 0/5).

Однако всякий раз, когда я запускаю свой код, используя класс DummyTest в качестве дочернего элемента, он возвращает «-nan (ind) / 5 вместо 5/5, и мой тестовый пример завершается неудачей

TEST_CASE("Scaling test returning 1/1 to 5/5")
{
    auto test = std::make_shared<DummyTest>(Score(1, 1)); // Tests that always returns 1/1
    Scaler scaler(test, 5);

    CHECK(scaler.run() == Score(5, 5));
}

это мой код:

class Score {
public:
    double value;
    double maximum;

    Score() : value(0), maximum(0) { }                          
    Score(double maximum) : value(0), maximum(maximum) { }      
    Score(double value, double maximum) : value(value), maximum(maximum) { }

    Score scale(double scale) const;
    bool success();
};

Score Score::scale(double scale) const {
    double a = (value / maximum) * scale;
    double b = scale;
    return Score(a , b);
}

class Test {
public:
    virtual Score run() const;
};

class DummyTest : public Test {
    Score score;

public:
    DummyTest(const Score& score) : score(score) { }

    Score run() const override {
        return score;
    }
};

class Scaler : public Test {
public:
    Test child;
    double maximum;

    Scaler(std::shared_ptr<Test> child, double maximum) : child(*child), maximum(maximum) { }

    Score run() const override;

};

Score Scaler::run() const {
    Score c = child.run().scale(maximum);
    return c;
}

1 Ответ

0 голосов
/ 25 августа 2018

Вы получили жертву так называемой нарезки объектов ; Я немного упростил код для лучшей иллюстрации (не имеет значения, если вы получаете указатель через умный указатель или необработанный указатель ...)

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().

...