列出和列出元素,存储位置

List and list elements, where are stored?

本文关键字:存储 位置 元素      更新时间:2023-10-16

给定这段代码:

#include <list>
(void) someFunction(void) {
    list <int> l;
    l.push_back(1);
}
  • 列表的元素存储在哪里?叠?堆?
  • 如何根据经验检查值是否在堆栈或堆中?
  • 这个函数可以返回列表吗?编辑如果我将函数声明为 list,函数可以返回列表而没有问题吗?

示例(返回列表(:

#include <list>
list<int> someFunction(void) {
    list <int> l;
    l.push_back(1);
}
...
l2 = someFunction();
l2.push_back(2);

列表的元素存储在哪里?叠?堆?

列表元素存储在堆中。您可以在调试器向下调用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);

在这种情况下,没有复制,它应该更快,因为只有一个列表创建。

列表的元素存储在哪里?叠?堆?

列表所持有的元素通常是动态分配的,因此它将在堆上。

这个函数可以返回列表吗?

不,它不能,您使用 void 的返回类型声明了您的函数。

如何根据经验检查值是否在堆栈或堆中?

真正确定的唯一方法是查看 std::list 实现,看看它到底在做什么。但是你真的不需要关心这个,因为它是一个实现细节。

您选择的答案在一件事上不正确...

返回列表不会调用列表的复制构造函数。

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)

您可以通过在函数中打印 &res 和在函数外部打印 &l 来仔细检查这一点。

编译器经过优化,可以在不复制的情况下传递返回的对象,否则返回的每个非指针对象都会被复制,这将是疯狂的。

如果您更改函数签名以返回列表,您确实可以返回列表;但是编译器会创建列表的副本,并且副本是调用方将接收的副本。

原始列表将在函数结束时销毁,但在此之前将进行复制,因此您将返回有效数据。如果您尝试欺骗系统并返回指针或引用以避免复制,那么此时您会遇到未定义的行为。

列表本身将在堆栈上,但其中包含的元素将在堆上。该列表将包含它在后台管理的指针,因此您完全不必担心存储。

其中一些可能会根据编译器和/或库作者的心血来潮而有所不同。

就像现在一样,编译器很有可能会检测到创建和填充列表的结果从未使用过,因此它将消除整个函数作为死代码。为了防止这种情况,您可以(例如(返回列表,而不仅仅是在函数结束时销毁它。

不过,这只会增加新的可能性。大多数编译器实现返回值优化 (RVO( 和命名返回值优化 (NRVO(。这些基本上意味着,当您/如果您返回一个值(例如您的list对象(,而不是在堆栈上创建一个对象并在返回时将其复制到目标,编译器会生成代码以生成返回后将要分配的对象。在这种情况下,该函数根本不会创建本地函数,而只会接收一个隐藏的指针,指向它将写入结果的位置。

std::list通常使用 std::allocate 为要存储的数据分配空间。但是,您可以指定不同的分配器供其使用。在这种情况下,空间几乎可以分配到任何地方。

虽然它更常见于其他(某种(容器,如std::string,但也有可能(至少在某些情况下(在对象本身中存储至少少量数据,并且仅在/如果溢出时在堆上分配空间。为了维护异常安全等,对此有一些限制,但在list<int>的情况下,这应该不是问题。例如,前十个int可能存储在list对象本身中,并且只有当您添加更多内容时,它才会在堆上分配空间。

返回函数声明如下: 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;
}

你可以这样做(只要我了解你的问题(

#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;
}