考虑以下C++代码:
#include <iostream>
using namespace std;
class Hello {
public:
Hello(const char* name) : name(name)
{}
void body() {
cout << name;
cout << " bodyn";
}
const char* name;
};
Hello* first;
void create() {
Hello wellington("Wellington");
first = &wellington;
}
int main() {
create();
Hello rob("Rob");
Hello james("James");
Hello donald("Donald");
// cout << "First points to: " << first << "n";
// cout << "Rob is at: " << &rob << "n";
// cout << "Donald is at: " << &donald << "n";
first->body();
}
很明显,在函数create
中,我们创建了一个Hello对象"Wellington",它是我们保存在first
中的指针,在这个过程结束后最终被销毁。之后,又创建了几个Hello对象,"Donald"是最后一个对象。
令人惊讶的是:尽管"first"指向一个"dead"对象,但body方法被正确执行。。就好像第一个指的是唐纳德。然而,指针不同,在first
指向的点和Rob所在的点之后的Donald指针。另一件奇怪的事情是,当包含注释行(打印指针(时,名称再次无法正确打印。最后,正如预期的那样,使用first->name
检查指向对象的名称只返回了胡言乱语。
那么,这里发生了什么?尽管指向的对象已不存在,但为什么使用Donald的名称正确执行body方法?
所写的是一个代码,它产生了一个所谓的未定义行为(UB(,部分是取消引用指向生命周期结束的对象的指针。
碰巧"first"捕获的死对象地址与现有对象的地址匹配。
当代码是UB时,任何事情都是可能的,这就是为什么不能编写它。
令人惊讶的是:尽管"first"指向一个"dead"对象,但body方法被正确执行。。
为什么这让你感到惊讶?通过无效指针进行访问具有未定义的行为。当行为未定义时,不能保证它不是"正确执行"(无论你期望的是什么"正确"(。
另一件奇怪的事情是,当包含注释行(打印指针(时,名称再次无法正确打印。
为什么这对你来说很奇怪?当行为未定义时,不能保证"再次正确打印"(无论您期望的是什么"正确"(。
当程序未定义时,程序的行为不会得到任何保证。
那么,这里发生了什么?尽管指向的对象已不存在,但为什么使用Donald的名称正确执行body方法?
程序的行为未定义。