智能指针可以被优化掉吗?

7

注意代码

...
{
    int* p = new int(0);
    std::unique_ptr<int> q(p);
    ...
    // make use of 'p'
}
...

在上述代码中,唯一的指针q仅用于在必要时释放pQ本身没有被使用。
由于在声明之后从未在该行以下使用过q,因此似乎可以立即释放q,从而使得p出现“在释放后使用”的情况。
问题是q是否保证在当前作用域结束前继续存在,或者编译器的优化器是否可以在此之前释放它?

3
只要不改变可观察行为,编译器可以进行任何优化 - 这就是“as-if”规则。自C++14以来,“new”和“delete”运算符就不再是可观察行为了,如果我没记错的话。 - L. F.
假设优化已经开启,编译器可能会执行SSA优化。它可以完全消除q,并将析构函数内联到类似于“finally”块的汇编代码中,在最后一次使用q或p之后。要确定,请查看您在平台上使用编译器优化后的代码汇编。 - Eljay
如果编译器在最后一次提到q或p之后销毁p,会产生很大的差异 :) 当然,我可以查看汇编代码,但我正在寻找不同(包括更新)编译器版本中的稳定行为。 - segfault
@L.F.:实际上,它是新表达式,可以省略,从而改变可观察行为。 - Jarod42
2个回答

5

根据“似乎规则”,只要可观察的行为相同,编译器就可以进行任何优化。

立即释放q/p是不被允许的,因为这样会使用悬空指针。

尽管它可以在作用域结束之前调用析构函数:

{
    int* p = new int(0);
    std::unique_ptr<int> q(p);
    ...
    // make use of 'p'
    ...
    // No longer use of p (and q)
    ...
    // Ok, can delete p/q now (as long there are no observable behaviors changes)
    ...
}

由于全局性可能会改变operator new/delete,因此编译器通常没有足够的信息(尽管链接器有),因此请考虑它们具有(潜在的)可观察行为(就像任何外部函数一样)。

c++14允许对new表达式进行一些省略/优化,因此

{
    delete new int(42);
    int* p1 = new int(0);
    int* p2 = new int(0);
    std::unique_ptr<int> q2(p2);
    std::unique_ptr<int> q1(p1);
    ...
    // make use of 'p1'/p2
    ...
}

可以“替换”为

{
    // delete new int(42); // optimized out
    std::unique_ptr<int[]> qs{new int [] {0, 0}}; // only one allocation instead of 2
    int* p1 = q->get();
    int* p2 = q->get() + 1;
    ...
    // make use of 'p1'/p2
    ...
}

0
我已经意识到了自己问题的答案:
在代码中
{
    int* p = new int(0);
    std::unique_ptr<int> q(p);
    ...
    // HERE
}

q 的析构函数保证在作用域退出时被调用 (此处)。
虽然编译器可以自由分配和释放变量的寄存器,但析构函数保证在特定位置 - 作用域退出时被调用。
我如何知道?
因为这就是 C++ 作用域守卫 的实现原理。通常,作用域守卫被用于在作用域退出时释放互斥锁 - 这是需要保证的事情。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接