c++ 基类引用的未定义行为

问题描述 投票:0回答:1

请查看我的代码。我有一个基类“System”和一个派生类“AdvancedSystem”。当访问基类引用时,输出是未定义和随机的,我不知道为什么。访问派生类给了我预期的输出。有什么想法吗?

#include <iostream>
#include <vector>

class System {
public:
    System() :
        someValue(12345)
    {}
 
    int someValue;
};

class AdvancedSystem : public System {
public:
    AdvancedSystem()
    {}
};

class Strategies {
public:
    Strategies(System& system) :
        system(system)
    {}

    System& system;
};

class Test {
public:
    Test() :
        system(),
        strategies(system)
    {}

    AdvancedSystem system;
    Strategies strategies;
};

class Tests {
public:
    void createTests() {
        for (int i = 0; i < 18; ++i) {
            values.emplace_back();
        }
    }

    std::vector<Test> values;
};

int main() {
    Tests tests;
    tests.createTests();
    std::cout << tests.values[0].system.someValue << "\n"; // outputs expected value of 12345
    std::cout << tests.values[0].strategies.system.someValue << "\n"; // outputs random number
}
c++ inheritance vector reference push-back
1个回答
0
投票

使用引用作为类成员通常是糟糕设计的标志。在你的情况下,情况更糟,因为你创建了自引用对象。在这种情况下,你不能依赖规则0,而应该依赖规则3规则5

向量上的每个

emplace_back
都会重新分配向量,并将值从旧分配移动到新分配。因为您遵循规则 0,所以复制/移动由自动生成的
default
代码处理;因此,
system
引用被按位复制,并且由于原始自引用对象被破坏,最终会得到一个悬空引用,它指向任何地方(随机内存位置)。这是UB的多功能保护壳。您可以通过
vector::reserve
方法预先分配向量来推迟灾难,但这只会隐藏实际问题并为灾难赢得时间。

最终的解决方案是修改您的设计,以防止在任何情况下出现悬空参考问题。您需要为您的类正确定义复制或移动构造函数。这就是为什么我建议您遵循第 1 段中的建议。

© www.soinside.com 2019 - 2024. All rights reserved.