2017-05-07 37 views
3

我試圖瞭解什麼似乎是一個怪異的行爲時分配一個新的值分配到堆棧上的對象(析構函數被調用兩次相同的數據集)。我就開始與代碼片段,其輸出:C++堆棧分配的對象分配和析構函數調用

class Foo { 
    public: 
     Foo(const string& name) : m_name(name) { 
      log("constructor"); 
     } 
     ~Foo() { 
      log("destructor"); 
     } 
     void hello() { 
      log("hello"); 
     } 
    private: 
     string m_name; 
     void log(const string& msg) { 
      cout << "Foo." << this << " [" << m_name << "] " << msg << endl; 
     } 
    }; 

    int main() { 

     { 
      Foo f {"f1"}; 
      f.hello(); 

      f = Foo {"f2"}; 
      f.hello(); 
     } 
     cout << "scope end" << endl; 
    } 

輸出:

Foo.0x7fff58c66a58 [f1] constructor 
    Foo.0x7fff58c66a58 [f1] hello 
    Foo.0x7fff58c66a18 [f2] constructor 
    Foo.0x7fff58c66a18 [f2] destructor 
    Foo.0x7fff58c66a58 [f2] hello 
    Foo.0x7fff58c66a58 [f2] destructor 
    scope end 

我希望發生:

  • 0X ...... 58被創建/初始化在堆棧上
  • 0x ... 18在堆棧上創建/初始化
  • Foo析構函數在0x ... 58上調用F1數據)
  • 富析構函數被調用於0X ... 18(具有f數據)

居然會發生什麼:

  • 0X ...... 58時生成/初始化堆棧
  • 0X ... 18獲取創建/初始化堆棧上從0X ... 18(F2)
  • 數據被複制到0X ... 58
  • 富析構函數被調用於0X ... 18(具有f數據)
  • 富析構函數被調用的0X ... 58(也具有f數據)

所以在最後,Foo析構函數被兩次調用相同的數據(f2)。很明顯,我錯過了一些關於內部工作原理的東西,所以有人可以請我指出正確的方向嗎?

+3

因爲您已經複製了一個臨時對象,它將'f'所保持的名稱從''f1''更改爲''f2''',所以不會看到「f1」'析構函數的消息。因此,你首先會看到臨時的「f2」破壞,然後原始實例(現在稱爲「f2」)被破壞。請注意,數據不一樣;它是具有相同值的副本。 –

+2

如果你的類有一個析構函數,它應該幾乎肯定也有一個拷貝構造函數和賦值運算符。 –

+0

並且(可能)移動構造函數和移動賦值操作符(「五個規則」)。 –

回答

4

您的實例˚F被分配富{「F2」}的副本,這不是一個新的建設

添加以下運算符=重寫以說明實際發生的情況。

Foo& Foo::operator=(const Foo& other) { 
    cout << "Foo::operator=(const Foo& other)" << endl; 
    m_name = other.m_name; 
    return *this; 
} 
3

之前創建第二Foo對象,你只需要在地址0x..58一個對象。

Address: 0x..58   Data: { m_name "f1" } 
Address: 0x..18   Data: unknown 

f = Foo {"f2"};首先創建在地址0x..18一個新的Foo對象,其m_name值爲"f2",並將其存儲。然後它將該對象分配給變量f

此分配不會破壞f中先前存在的對象,它只會將數據成員複製到其中。由於Foo對象只有一個數據成員m_name,因此該作業僅將第二個對象的m_name複製到第一個對象中。

Address: 0x..58   Data: { m_name "f2" } 
Address: 0x..18   Data: { m_name "f2" } 

然後爲每個這些對象調用析構函數。輸出並不意味着同一個對象被銷燬兩次,它只是意味着兩個對象都有相同的m_name