到目前为止所有的答案都告诉你如何正确地做到这一点,但我想我会给你一些更多的细节,为什么你的代码不起作用。因此,正如其他人所指出的那样,您的“getter”正在按值返回列表。这是(主要是)C++特有的东西:程序员必须明确指定是要通过值还是通过引用传递对象。其他编程语言,例如 Java,将(几乎)总是通过引用传递。假设您分配了一个这样的变量:
MyClass a;
MyClass b = a;
在许多语言中,赋值意味着:b
引用指向a
. 然后,您将能够调用 上的方法b
,并且它的行为就像它一样a
。
另一方面,在 C++ 中,这意味着:“创建第二个对象b
,然后将所有a
的状态复制到b
(忽略 MyClass 具有复制构造函数的可能性,这与本解释无关)。现在对于列表,这个意味着每个元素都将被复制到一个新创建的列表中!(除了其他含义之外,这可能是一个性能问题)。
另一方面,如果您告诉编译器引用 a:
MyClass& b = a;
那么这b
确实会表现得好像它是一个。不会复制任何状态,改变b
会改变a
。
好的,现在回到您的代码示例。在第一个版本中,您有以下行:
// Creates an invalid iterator!
std::list<OtherClass *>::iterator itB = inst.getL().begin();
这实际上是一堆不同的东西。调用将创建一个新列表并将's 列表成员的inst.getL()
所有内容复制到其中。inst
然后,它将获得该副本的迭代器。之后,副本本身被销毁,迭代器将失效。为什么?因为您没有将列表的副本分配给任何东西。new
在 C++ 中,超出范围的堆栈分配对象(即不是使用创建的)将被破坏。简单地说,一旦对象不再可以通过其名称访问,就会发生“超出范围”:
{ // 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
如果您丢弃函数的返回值,也会发生这种情况,例如编写:
inst.getL();
这将创建您的列表的副本,然后再次将其销毁。
现在,为什么您的第二个示例有效?因为您将列表的副本分配给临时变量,所以它们保持在范围内:
std::list<OtherClass *> l = inst.getL();
来自“getter”调用的临时对象被存储到l
(暂时忽略赋值运算符、RVO 等),并且获得的所有迭代器l
现在都将有效,直到l
超出范围。
std::list<OtherClass *>::iterator itB = l.begin(); // valid
所以这行得通,尽管可能不像您预期的那样:迭代器在您的列表副本上运行,而不是在实际数据上运行。有时这可能是您想要的 - 但在您的情况下,您需要其他答案所建议的参考。
希望这有助于为您澄清一点。