鉴于这段代码:
#include <list>
(void) someFunction(void) {
list <int> l;
l.push_back(1);
}
示例(返回列表):
#include <list>
list<int> someFunction(void) {
list <int> l;
l.push_back(1);
}
...
l2 = someFunction();
l2.push_back(2);
答案 0 :(得分:5)
列表中的元素存储在哪里?堆?堆?
列表元素存储在堆上。你可以在push_back方法调用的调试器后面看到这个。最容易看到的是存储对象而不是POD类型,并记录构造函数。你需要复制构造函数,因为它们被复制了。分配发生在模板参数分配器中,您可以指定它,也可以不指定它,它将使用默认的堆分配。
如何凭经验检查值是在堆栈还是堆中?
您可以使用堆栈中的push_back元素进行检查:
std::list<int> my_list;
int a = 10;
my_list.push_back(a);
a = 11;
assert(*my_list.begin() == 10);
此函数可以返回列表吗?
在C ++中,有两种传递数据的方法:引用或按值。如果你的功能看起来像这样,
list<int> func()
{
list<int> res;
res.push_back(10);
return res;
}
然后你按值传递列表,这意味着编译器将调用列表的复制构造函数,该构造函数也复制列表中的所有值。当函数返回时,在复制列表之后,将调用“res”列表的析构函数,释放其所有元素。但是,如果你这样做:
list<int>& func()
{
list<int> res;
res.push_back(10);
return res;
}
当您返回对“res”列表的引用时,您的代码将失败,该列表将在其作用域的末尾被销毁,因此您的引用将无效。
第一个解决方案的问题可能是性能问题。你也可以在没有复制构造函数调用的情况下这样做:
void func(list<int>& res)
{
res.push_back(10);
}
list<int> list_to_fill;
func(list_to_fill);
在这种情况下,没有复制,它应该更快,因为只有一个列表创建。
答案 1 :(得分:3)
列表中的元素存储在哪里?堆?堆?
列表中保存的元素通常是动态分配的,因此它将在堆上。
此函数可以返回列表吗?
不,它不能,你声明你的函数返回类型为void。
如何凭经验检查值是在堆栈还是堆中?
唯一可靠的方法是查看std :: list实现,看看它到底在做什么。但是你真的不需要关心这个,因为它是一个实现细节。
答案 2 :(得分:1)
如果更改了函数签名以返回列表,则确实可以返回列表;但是编译器会对列表进行复制,并且副本是调用者将接收的副本。
原始列表将在函数结束时销毁,但在此之前将进行复制,因此您将返回有效数据。如果你试图欺骗系统并返回指针或引用而不是复制,那就是当你遇到未定义的行为时。
列表本身将在堆栈中,但其中包含的元素将在堆上。该列表将包含它在幕后管理的指针,因此您根本不必担心存储。
答案 3 :(得分:1)
其中一些可能会因编译器和/或库作者的异想天开而异。
就像现在一样,编译器很可能会检测到创建和填充列表的结果从未被使用过,因此它会将整个函数作为死代码消除。为了防止这种情况,您可以(例如)返回列表,而不是仅仅在函数结束时将其销毁。
这只会增加新的可能性。大多数编译器实现返回值优化(RVO)和命名返回值优化(NRVO)。这些基本上意味着当/如果你返回一个值(例如你的list
对象)而不是在堆栈上创建一个对象并在它返回时将它复制到目标,编译器生成代码以生成返回后将分配的对象。在这种情况下,该函数根本不会创建一个本地函数,只会收到一个指向它将写入结果的位置的隐藏指针。
std::list
通常使用std::allocate
为要存储的数据分配空间。但是,您可以为其指定不同的分配器。在这种情况下,空间几乎可以分配到任何地方。
虽然它对std::string
之类的其他(某种)容器更常见,但也有可能(至少在某些情况下)在对象本身中存储至少少量数据,并且只在其上分配空间当/如果溢出时堆。这对于维护异常安全等有一些限制,但在list<int>
的情况下,它应该不是问题。例如,前十个int
可能存储在list
对象本身中,并且只有当/如果添加更多时,它才会在堆上分配空间。
答案 4 :(得分:1)
返回函数声明如下:list<int> somefunc() {list<int> L; return L; }
。
使用list<int>::iterator
检查值。
像这样:
#include <iostream>
#include <list>
using namespace std;
list<int> someReturnFunc(int listSize)
{
list<int> myList;
for (int g=0; g< listSize; g++) myList.push_back(g);
return myList;
}
int main ()
{
list<int> yourList;
list<int>::iterator i;
yourList = someReturnFunc(15);
for(i=yourList.begin(); i != yourList.end(); ++i) cout << *i << " ";
cout << endl;
return 0;
}
答案 5 :(得分:1)
你可以这样做(只要我理解你的问题)
#include <iostream>
#include <list>
using namespace std;
list<int> & fun()
{
list<int> & temp = *(new list<int>);
temp.push_back(4);
// you can do other operations on the list
return temp; // you pass the reference, any list will not be destroyed neither copied.
}
int main()
{
list<int> & my_list = fun();
cout << *(my_list.begin()) << endl;
}
答案 6 :(得分:1)
您选择的答案不正确1件事......
返回列表不会调用列表的复制构造函数。
list<int> func()
{
list<int> res; // &res returns 0x7fff183f0900
res.push_back(10);
return res;
}
list<int> l = func(); // &l also returns 0x7fff183f0900 (no copy)
您可以通过在功能中打印&amp;和在功能外部&amp; l来仔细检查。
编译器经过优化,可以在不进行复制的情况下传递返回的对象,否则会复制返回的每个非指针对象,这将是疯狂的。