2015-06-26 13 views
3

考虑下面的代码:新表达式求值顺序(指针分配)

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:

  • p被分配给新的指向表达呼叫操作员新分配的内存
  • Class“sc`tor被调用,分配的内存被传递给它

示例2:

  • 新表达式调用操作符new
  • Class“sc`tor被调用,分配的内存传递给它
  • p被分配到指向新分配的内存
+0

为什么不在建设者里面睡一觉,看看自己,分享所获得的知识? :) – Ajay

+0

根据http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf,答案是“否”:在所述存储器中构建对象之前,您可能会指向分配的存储器。 – Quentin

+0

没有保证(实际上你的例子调用未定义的行为)。这正是p必须是原子变量的原因之一。原子变量的默认排序约束也将保证构造函数在分配给p之前是完整的。 Herb Sutter的原子武器谈话对解释这种行为做了很好的工作。 – John5342

回答

6

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

C++ 11 N3337草案,

[intro.multithread]/4:

两个表达评价冲突如果它们中的一个修改的存储器位置(1.7),而另一个 访问或修改相同的内存位置。

[intro.multithread]/21:

一个程序的执行包含数据争,如果它包含在不同的线程两个相互矛盾的操作,其中 至少一个不是原子,而不是在另一个之前发生。任何这样的数据竞争导致 未定义的行为。

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


至于p = new Class;执行顺序,这就像在你的例2中,因为第一new Class进行评估,然后分配情况(提供Classoperator new构造函数没有抛出异常)。

[expr。屁股]/1:

在所有情况下,分配的右和左操作数的值 计算后测序,并赋值表达式的值计算之前。

+0

好吧,那么怎么样如果我们把第二条线放在一边。标准是否在'p'将被分配时执行? (在施工结束之前或开始之前)? –

+0

@AlejandroFreeman我更新了答案。 –

0

不,标准并不保证这样的事情。

要解决它,你需要有你的对象的建设和分配指针之间的内存屏障,以便有线程间的之前发生它们之间的关系:

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); 
+0

这是错误的。 *在所有情况下,赋值在右边和左边操作数* - [expr.ass]/1 –

+0

@Anton的值 的计算后排序:这就是C++标准所说的,现在如何重新排序CPU和方式内存是从另一个线程观察?如果顺序 - 之后意味着跨线程的正确可见性顺序,那么我们就不需要语言中的原子。 –

+0

另外,如果我的理解是正确的,那么编译器仍然可以重新排列指令,即使在“之前已排序”之间也可以重新排序指令,前提是它可以保证在当前执行线程中观察到相同的结果。 –