首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如果我从getter生成迭代器,则程序中止

如果我从getter生成迭代器,则程序中止
EN

Stack Overflow用户
提问于 2013-01-20 07:17:17
回答 6查看 555关注 0票数 0

这是一个奇怪的行为,我不明白我有。

我有一个带有列表的a类,上面有一个getter:

代码语言:javascript
运行
复制
class A
{
  private:
   std::list<OtherClass *> l;
  public:
   std::list<OtherClass *> getL()
   {
     return l;
   }
}

然后,如果我这样做:

代码语言:javascript
运行
复制
A inst;
std::list<OtherClass *>::iterator itB = inst.getL().begin();
std::list<OtherClass *>::iterator itE = inst.getL().end();
for (; itB != itE; ++itB) // Instant ABORT !

但如果我这么做了:

代码语言:javascript
运行
复制
A inst;
std::list<OtherClass *> l = inst.getL();
std::list<OtherClass *>::iterator itB = l.begin();
std::list<OtherClass *>::iterator itE = l.end();
for (; itB != itE; ++itB) // It works now !

有人能给我解释一下为什么会这样吗?为什么我必须经历像这样的临时变量才不会中止呢?提前谢谢你!

EN

Stack Overflow用户

发布于 2013-01-20 07:46:11

到目前为止,所有的答案都告诉你如何正确地做这件事,但我想我应该给你一些为什么你的代码不能工作的更多细节。因此,正如其他人所指出的,您的"getter“是按值返回列表。这是(主要)C++特有的东西:作为程序员,您必须显式地指定是通过值传递对象还是通过引用传递对象。其他编程语言,如Java,将(几乎)总是通过引用传递。假设您像这样分配了一个变量:

代码语言:javascript
运行
复制
MyClass a;
MyClass b = a;

在许多语言中,赋值的意思是:使b成为指向a的引用。然后,您将能够调用b上的方法,并且它的行为就像是a一样。

另一方面,在C++中,这意味着:“创建第二个对象b,然后将a的所有状态复制到b中(忽略MyClass具有复制构造函数的可能性,这与此解释无关)。现在对于列表,这意味着每个元素都将被复制到新创建的列表中!(除了其他影响,这可能是一个性能问题)。”

另一方面,如果您告诉编译器引用一个:

代码语言:javascript
运行
复制
MyClass& b = a;

那么这个b的行为就好像它是一个。不会复制任何状态,并且更改b将更改a

好了,现在回到你的代码示例。在第一个版本中,您具有以下行:

代码语言:javascript
运行
复制
// Creates an invalid iterator!
std::list<OtherClass *>::iterator itB = inst.getL().begin();

这实际上是一堆不同的东西。对inst.getL()的调用将创建一个新的列表,并将inst的列表成员的所有内容复制到其中。然后,它将获得该副本的迭代器。之后,副本本身将被销毁,迭代器将变得无效。为什么?因为您没有将列表的副本分配给任何对象。在C++中,超出作用域的堆栈分配对象(即不是使用new创建的对象)将被析构。简单地说,当对象不能再通过其名称访问时,就会发生“超出作用域”:

代码语言:javascript
运行
复制
{ // Begin scope
    MyClass o; 
    // Inside the braces, it's possible to refer to o:
    o.doSomething();
} // End scope
o.doSomething() // Will be an error, as o is not "known" anymore

如果丢弃函数的返回值,也会发生这种情况,比如这样写:

代码语言:javascript
运行
复制
inst.getL(); 

这将创建一个列表的副本,然后再次销毁它。

现在,为什么你的第二个例子是可行的?因为您将列表的副本分配给tempory变量,所以它们留在作用域中:

代码语言:javascript
运行
复制
std::list<OtherClass *> l = inst.getL();

来自"getter“调用的临时对象被存储到l中(暂时忽略赋值操作符、RVO等),并且为l获得的所有迭代器现在都将有效,直到l超出作用域。

代码语言:javascript
运行
复制
std::list<OtherClass *>::iterator itB = l.begin(); // valid

因此,这是可行的,尽管可能与您预期的不同:迭代器是在列表的副本上操作,而不是在实际数据上操作。这有时可能是你想要的--但在你的情况下,你想要其他答案所建议的参考。

希望这篇文章能帮你弄清楚一点。

票数 3
EN
查看全部 6 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14419944

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档