新的表达式求值顺序(指针赋值)

new expression evaluation order (pointer assignment)

本文关键字:指针 赋值 顺序 表达式      更新时间:2023-10-16

考虑以下代码:

Class* p = nullptr; //global var

此代码由线程1:执行

p = new Class;

此代码在线程2:上执行

if (p != nullptr) ...; // does the standard gurantee that the pointer will be assigned only after object is constructed ?

我的问题是,当p被分配到分配的内存时,标准是否强制执行?示例1:

  • new表达式调用运算符new
  • p被指派指向新分配的存储器
  • 调用Class的c`tor并将分配的内存传递给它

示例2:

  • new表达式调用运算符new
  • 调用Class的c`tor并将分配的内存传递给它
  • p被指派指向新分配的存储器

根据标准,如果两个线程中的这些操作不同步,则行为是未定义的。

C++11草案N3337,

[介绍.多线程]/4:

两个表达式求值冲突如果其中一个修改内存位置(1.7)而另一个修改访问或修改相同的存储器位置。

[介绍.多线程]/21:

程序的执行包含数据竞赛,如果它在不同线程中包含两个冲突的操作,其中至少有一个不是原子的,也没有发生在另一个之前。任何此类数据竞赛都会导致未定义的行为。

C++14中相应的引用基本上是相同的。


至于p = new Class;的执行顺序,就像您的示例2中一样,因为首先计算new Class,然后进行赋值(前提是Classoperator new的构造函数没有抛出异常)。

[expr.ass]/1:

在所有情况下,赋值都在值之后排序右操作数和左操作数的计算以及在赋值表达式的值计算之前。

不,标准不能保证这样的东西。

为了解决这个问题,你需要在对象的构造和指针的分配之间设置一个内存屏障,这样在它们之间的关系之前就会发生线程间的事情:

Class* tmp = new Class();
// you need a memory barrier here
p = tmp;

在c++11中,您使用std::atomic引入内存屏障:

std::atomic<Class*> p;

在这种情况下,最好使用store(),而不是分配:

p.store(tmp, std::memory_order_release);