提问人:Miroslav Krajcir 提问时间:11/14/2023 更新时间:11/14/2023 访问量:107
对基类的引用的 C++ 未定义行为
c++ undefined behavior of a reference to a base class
问:
请看我的代码。我有一个基类“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
}
答:
3赞
Red.Wave
11/14/2023
#1
使用引用作为类成员通常是设计不良的标志。在你的情况下,情况更糟,因为你创建了自我防护的对象。在这样的 senarios 中,你不能依赖规则 0,而应该依赖规则 3 或规则 5。
在向量上的每一个向量上,向量都会被重新分配,并且值会从旧分配移动到新分配。由于遵循规则 0,因此复制/移动由自动生成的代码处理;因此,引用是按位复制的,并且由于原始的自引用对象被破坏,因此最终会得到一个悬空引用,引用无处(随机内存位置)。这是UB的通用案例。您可以通过预先分配向量 via 方法来推迟灾难,但这只会隐藏实际问题并为灾难争取时间。emplace_back
default
system
vector::reserve
最终的解决方案是修改您的设计,以防止在任何情况下出现悬空参考问题。您需要为您的类正确定义复制或移动构造函数。这就是为什么我建议您遵循第 1 段中的建议。
评论
values
std::list
大多数时候性能较差,但有时这并不重要,它有助于稳定的迭代器(列表不会重新分配)Strategies
System
Test
System
Strategies