2016-08-16 3 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 
$ 

bobjsベクトルでAオブジェクトの状態がb.DoTheThing()コールとその後のprint文の間で変化しています。私の質問は何が起こっているのですか? Aオブジェクトdataベクターが何とかスコープから外れて削除されているか、またはおそらくAオブジェクト全体ですか?それはスコープの問題のように思えます - おそらく単純なものでさえ - でも、私はC++でプログラミングして以来十分ではありません。他の方法でb.DoTheThing()を呼び出した後に、dataベクトルの内容を持続させるにはどうすればよいですか?

答えて

6

」になります私の質問は何が起こっているのですか? "

実際にobjsの個別のコピーを個々の一時的なobjに作成しています。それらの一時的なものは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 
    ... 
}; 

explicitをキャッチするコンパイルエラーを生成する方法を示しており、demoですforのループで誤ってコピーを作成することがあります。


explicitには独自の構文制限があります。それらのいくつかは解決することができます&いくつかすることはできません。

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

+0

コピーコンストラクタ' explicit'を作ることは他の問題を引き起こす可能性があります。コピーを作成する場所を理解するための言語を学び、必要に応じてコピーを避けるだけです。 –

+0

@JonathanWakely、そのような既知の問題の1つはRVOのことです。それは私が答えを更新した特定の状況で解決することができます。はい、「明示的」には独自の制限がありますが、私はそれが価値があると思います。私は数週間前にこのような状況にあったため、デバッグは本当に困難でした。 '#ifdef/#endif'sを作成し、' #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 (auto& obj : b.objs) 

を使用すると、参照することにより、容器内の実際のオブジェクトを扱うこのように。あなたのようにそれを宣言すると は:

for (auto obj : b.objs) 

obj BのobjsvectorAオブジェクトの状態がb.DoTheThing()コールとその後のprint文の間で変化しているいくつかの理由でコピー

+0

驚くばかりに変更しています。私はそれが簡単だと分かっていました。ありがとう! –

+0

あなたは 'for_each'と次のステップで楽しむことができます:-) –

0

の代わりにあなたが参照によって受け取る使うべき値で受け取る:1つの、このような問題のために、次の質問(それが解決していますか&)を参照してください。コードの一部の上

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

、値によって受信

for (auto& obj : b.objs) { 
     ........ 
    } 
関連する問題