2016-08-16 56 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) { 
     ........ 
    } 
相关问题