2015-06-26 35 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

另外,如果我的理解是正確的,那麼編譯器仍然可以重新排列指令,即使在「之前已排序」之間也可以重新排序指令,前提是它可以保證在當前執行線程中觀察到相同的結果。 –