注意代码
...
{
int* p = new int(0);
std::unique_ptr<int> q(p);
...
// make use of 'p'
}
...
在上述代码中,唯一的指针q仅用于在必要时释放p。 Q本身没有被使用。
由于在声明之后从未在该行以下使用过q,因此似乎可以立即释放q,从而使得p出现“在释放后使用”的情况。
问题是q是否保证在当前作用域结束前继续存在,或者编译器的优化器是否可以在此之前释放它?
注意代码
...
{
int* p = new int(0);
std::unique_ptr<int> q(p);
...
// make use of 'p'
}
...
根据“似乎规则”,只要可观察的行为相同,编译器就可以进行任何优化。
立即释放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
...
}
{
int* p = new int(0);
std::unique_ptr<int> q(p);
...
// HERE
}
q 的析构函数保证在作用域退出时被调用 (此处)。
虽然编译器可以自由分配和释放变量的寄存器,但析构函数保证在特定位置 - 作用域退出时被调用。
我如何知道?
因为这就是 C++ 作用域守卫 的实现原理。通常,作用域守卫被用于在作用域退出时释放互斥锁 - 这是需要保证的事情。