指向临时对象的指针

Pointer to temporary object

为什么这段代码可以正常工作?

struct A {
    std::string value = "test"s;
    
    A() { std::cout << "created" << std::endl; }
    ~A() { std::cout << "destroyed" << std::endl; }
};

int main() { 
    A* ptr = nullptr;
    
    {
        A a;
        ptr = &a;
    }
    
    std::cout << ptr->value << endl; // "test"
}

输出:

或者这个例子:

struct A {
    const std::string* str = nullptr;
    
    A(const std::string& s) : str(&s) {}
};

int main()
{
    A a = std::string("hello world");
    std::cout << *a.str;

    return 0;
}

输出:

在第一种情况下,在我看来,当对象 A 被销毁时,内容将变得无效,但我能够检索到字符串。

在第二种情况下,我通过常量引用获取右值,但只要引用有效,延长对象的生命周期就应该有效。我以为构造函数工作后,字符串应该已经被销毁了。为什么这没有发生?

两个代码都有undefined behavior.

这里:

{
    A a;
    ptr = &a;
}

std::cout << ptr->value << endl; // "test"

ptr 一旦 a 超出范围并被销毁就变得无效。

与第二个示例类似,您也解除了对无效指针的引用,因为临时字符串在调用构造函数后消失了。

C++ 没有定义当你做一些没有定义的事情时会发生什么(有道理,不是吗?;)。相反,它相当明确地说,当您做某些错误的事情时,代码的行为是未定义的。取消引用无效指针是未定义的。代码的输出可以是任何东西。编译器不需要诊断未定义的行为,但有一些功能和工具可以提供帮助。使用 gcc,-fsanitize=address 检测到您的两个代码中的问题:https://godbolt.org/z/osc9ah1jo and https://godbolt.org/z/334qaKzb9.

Why is this not happening?

该程序(片段 1 和片段 2)具有 未定义的行为。在第一个片段中,对象 a 一旦超出范围就被销毁,因此 ptr 变成了一个 悬空指针 。取消引用悬空指针(通过编写表达式 ptr->value 来实现)是未定义的行为。

在第二个片段中,临时字符串在构造函数调用完成后被销毁。所以,a.str 又是一个悬挂指针。取消引用这个悬挂指针(你通过编写表达式 *a.str 来完成)是未定义的行为。

Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely(or make conclusions based) on the output of a program that has undefined behavior.

所以您看到(也许看到)的输出是未定义行为的结果。正如我所说,不要依赖具有 UB 的程序的输出。程序可能会崩溃。

例如,here the program seems to give correct output but here它给出垃圾输出。

因此,使程序正确的第一步是删除 UB。 然后并且只有那时你可以开始对程序的输出进行推理。


1有关未定义行为的更技术准确的定义,请参阅 this 其中提到:没有对程序行为的限制.