2016-08-16 37 views
2

下面的虛擬程序模仿我正在排除故障的另一個程序的行爲。爲什麼方法範圍結束後矢量內容發生了變化?

#include <iostream> 
#include <vector> 

class A 
{ 
public: 
    std::vector<int> data; 

    void DoTheThing() 
    { 
     while (data.size() < 10) { 
      data.push_back(1); 
     } 
    } 
}; 

class B 
{ 
public: 
    std::vector<A> objs; 

    B() 
    { 
     A one, two, three; 
     objs.push_back(one); 
     objs.push_back(two); 
     objs.push_back(three); 
    } 

    void DoTheThing() 
    { 
     for (auto obj: objs) { 
      obj.DoTheThing(); 
      std::cout << "DEBUG length during=" << obj.data.size() << std::endl; 
     } 
    } 
}; 

int main() 
{ 
    B b; 
    b.DoTheThing(); 
    for (auto obj : b.objs) { 
     std::cout << "DEBUG length after=" << obj.data.size() << std::endl; 
    } 
} 

予編譯和作爲運行:

$ g++ -Wall --std=c++11 -o test test.cpp 
$ ./test 
DEBUG length during=10 
DEBUG length during=10 
DEBUG length during=10 
DEBUG length after=0 
DEBUG length after=0 
DEBUG length after=0 
$ 

出於某種原因,A物體在bobjs向量狀態在b.DoTheThing()呼叫和隨後的打印語句之間改變。我的問題是發生了什麼A對象data矢量以某種方式超出範圍並被刪除,或者可能是整個A對象?這似乎是一個範圍問題 - 可能甚至是一個簡單的問題 - 但它已經足夠長,因爲我用C++編程,我不確定。在其他方法中調用b.DoTheThing()後,如何使data向量的內容保持不變?

回答

6

「出於某種原因,A對象在b的objsvectorb.DoTheThing()呼叫和後面的print語句之間改變狀態我的問題是怎麼回事?

void DoTheThing() 
{ 
    for(auto obj : objs) 
    // ^^^^^^^ receive by value due to implicit copy 

您實際上正在將單獨的objs複製到單個臨時obj s中。在B::DoThething()完成後,這些臨時對象被銷燬。爲了避免拷貝,使用參考

for(auto& obj : objs) 
    // ^^^^^^^ receive by reference 

同樣是在main()類似的循環也如此。


真正的問題可以是:「如何避免這種事故?「

如果你能負擔得起,那麼使拷貝構造函數explicit避免隱複製

class A { 
public: 
    A() = default;  
    explicit A(const A&) = default; // implicit copy is avoided 
    A (A&&) = default; // to avoid RVO related issues 
    ... 
}; 

這裏是一個demo,這顯示瞭如何將explicit產生編譯錯誤趕上在for迴路中出現意外複印的問題


explicit帶來了自己的語法限制。其中一些可以解決&一些不能。請參考以下問題1個這樣的問題(&它是如何解決):

What is the effect of 'explicit' keyword on the Return Value Optimization (RVO)?

+0

複製構造函數'explicit'可能會導致其他問題。最好只學習語言以瞭解製作副本的位置,並在需要時避開它。 –

+0

@JonathanWakely,其中一個已知問題是RVO的事情。在某些情況下可以解決這個問題,我已經在答案中更新了。是的,「顯性」有其自身的侷限性,但我認爲它們值得。幾周前我已經遇到過這種情況,調試真的很困難。我們也可以在它下面有一個'#ifdef /#endif'並創建一個'#define conditional_explicit explicit'。這對最終檢查任何這樣的副本並恢復正常很有用。 – iammilind

2

的問題是在for循環的範圍:

void DoTheThing() 
    { 
     for (auto obj: objs) { 
      obj.DoTheThing(); 
      std::cout << "DEBUG length during=" << obj.data.size() << std::endl; 
     } 
    } 

要複製的A對象,並做修改的副本,而不是在原來的一個。

將其更改爲:

for (auto& obj: objs) { 
    ... 
} 

,並預期它應該工作。

2

你必須改變你的for循環如下:

for (auto& obj : b.objs) 

這樣您參考處理在容器中的實際對象。 如果你將它聲明爲:

for (auto obj : b.objs) 

obj將是一個副本

+0

真棒。我知道這很簡單。謝謝! –

+0

你可以用'for_each'和下一步獲得樂趣:-) –

0

而不是接收價值,你應該使用基準接收。

for (auto obj : b.objs) { 
     std::cout << "DEBUG length after=" << obj.data.size() << std::endl; 
    } 

上面一段代碼是接收時,它更改爲

for (auto& obj : b.objs) { 
     ........ 
    } 
相關問題