通过基类引用派生类后打印的错误值
Bad value printed after referring to derived class through base class
在实际应用程序中遇到类似情况后,我决定制作一个演示,该演示表明,如果我将派生类存储为指向基类的指针,并调用虚拟方法,派生类的行为将不正确。请参阅下面的代码:
struct IntWrapper { int value; };
struct Base {
virtual ~Base() = default;
virtual void myMethod() = 0;
};
struct Foo : public Base {
const IntWrapper& x;
Foo(const IntWrapper& x) : x(x) {}
void myMethod() override {
std::cout << std::to_string(x.value) << std::endl;
}
};
struct Bar : public Base {
const IntWrapper& x;
const IntWrapper& y;
Bar(const IntWrapper& x, const IntWrapper& y) : x(x), y(y) {}
void myMethod() override {
std::cout << std::to_string(x.value) << " " << std::to_string(y.value) << std::endl;
}
};
int main()
{
Base* foo = new Foo(IntWrapper{3});
Base* bar = new Bar(IntWrapper{5}, IntWrapper{42});
foo->myMethod();
bar->myMethod();
return 0;
}
预期输出为:
3
5 42
相反,我收到:
42
5 42
有趣的是,如果我在 Foo 和 Bar 类中用原始 int 替换 IntWrapper 引用,则打印的值将是正确的。有人可以向我解释为什么会发生这种行为吗?
Base* foo = new Foo(IntWrapper{3});
Base* bar = new Bar(IntWrapper{5}, IntWrapper{42});
您正在创建一个临时IntWrapper
,该临时作为对构造函数的引用传递,用于保存该引用供以后使用。由于临时在构造函数之后被破坏,因此您在foo
和bar
中的引用无效,并且您正在调用未定义的行为,这意味着任何操作都可能发生。
您需要创建将传递给构造函数的IntWrapper
变量,如下所示:
IntWrapper x{3};
IntWrapper y{5};
IntWrapper z{42};
Base* foo = new Foo(x);
Base* bar = new Bar(y,z);
或者,您的类应该复制传递的IntWrapper
,而不是保留对它们的引用。
您所有的成员引用都悬而未决,因此您有 UB。
和
Base* foo = new Foo(IntWrapper{3});
创建一个临时 IntWrapper(在完整表达式的末尾销毁(;在 foo 中保存对此临时的引用。当您下次使用 foo 时,保存的引用悬而未决(因为临时引用已被销毁(,并且您有未定义的行为。- 理查德·克里滕
我在 OP 的示例代码中添加了一些 printf 调试以进行演示。现在,评论中描述的效果变得非常明显(恕我直言(:
#include <iostream>
struct IntWrapper {
int value;
IntWrapper(int value): value(value)
{
std::cout << "IntWrapper::IntWrapper(" << value << ")n";
}
~IntWrapper()
{
std::cout << "IntWrapper::~IntWrapper(" << value << ")n";
}
};
struct Base {
Base(const char *text)
{
std::cout << text << "::" << text << "()n";
}
virtual ~Base() = default;
virtual void myMethod() = 0;
};
struct Foo : public Base {
const IntWrapper& x;
Foo(const IntWrapper& x): Base("Foo"), x(x) { }
void myMethod() override {
std::cout << std::to_string(x.value) << std::endl;
}
};
struct Bar : public Base {
const IntWrapper& x;
const IntWrapper& y;
Bar(const IntWrapper& x, const IntWrapper& y): Base("Bar"), x(x), y(y) {}
void myMethod() override {
std::cout << std::to_string(x.value) << " " << std::to_string(y.value) << std::endl;
}
};
int main()
{
Base* foo = new Foo(IntWrapper{3});
Base* bar = new Bar(IntWrapper{5}, IntWrapper{42});
std::cout << "foo->myMethod();n";
foo->myMethod();
std::cout << "bar->myMethod();n";
bar->myMethod();
return 0;
}
输出:
IntWrapper::IntWrapper(3)
Foo::Foo()
IntWrapper::~IntWrapper(3)
IntWrapper::IntWrapper(5)
IntWrapper::IntWrapper(42)
Bar::Bar()
IntWrapper::~IntWrapper(42)
IntWrapper::~IntWrapper(5)
foo->myMethod();
3
bar->myMethod();
5 42
科里鲁的现场演示
注意:
样本与预期输出匹配的事实不应被误解。它仍然是未定义的行为。
相关文章:
- 为什么没有打印错误消息
- 插入排序打印错误的输出
- 当我输入一个字母时,cin 输入(输入是一个 int),而不是打印错误一次,它打印正确一次,然后在循环的其余部分打印 i
- 使用使用运算符<<并打印错误值 c++ 的类进行编程
- cLion & cMake 不调试/打印错误
- 如何使用适用于 Lua 的 C API 在控制台中打印错误
- 为什么我的代码打印错误的密文
- 声明数组和打印[错误?]
- 为什么在这种情况下打印错误
- q调试打印错误的数字
- 阿姆斯特朗数字打印错误
- C++ 链表打印错误
- 长双精度在 MinGW 上打印错误
- C++ <ctime> 打印错误的日期
- 用"fprintf"打印错误后调用"getchar()"有什么意义?
- C++类型包装器 printf 打印错误的浮点值
- 如何让犰狳函数在反转奇异矩阵时不打印错误
- 缩短的数组打印错误的结果
- 打印错误的链表c++
- cout 中的打印错误