为什么这段代码返回-nan(ind)?C ++

时间:2018-08-25 09:56:58

标签: c++ testing scale

我正在尝试编写一个带有子测试的Scaler类,该子测试具有一个Score。

缩放器使用其孩子的分数类的缩放方法来重新缩放其值(例如,缩放比例5 = 5/5的1/1或缩放比例5 = 0/5的0/1)。

但是,每当我以DummyTest类作为子代运行代码时,它都会返回“ -nan(ind)/ 5而不是5/5和我的test case fails

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 :(得分:3)

您已经成为所谓的object slicing的受害者;我对代码进行了一些简化以更好地说明(通过智能指针还是原始指针接收指针都没有关系……)

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()中有一个悬挂的引用。