2012-01-16 3 views
1

Учитывая этот кусок кода:Элементы списка и списка, где хранятся?

#include <list> 
(void) someFunction(void) { 
    list <int> l; 
    l.push_back(1); 
} 
  • Где элементы списка хранятся? Стек? Heap?
  • Как сделать, чтобы эмпирически проверить, что значения находятся в стеке или куче?
  • Эта функция может возвращать список? Отредактировано Если я объявляю функцию как список, можно ли возвращать список функций без проблем?

Sample (возвращение список):

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

... 
l2 = someFunction(); 
l2.push_back(2); 
+5

Фактический объект списка будет храниться в стеке (как все локальные переменные и аргументы функции), в то время как элементы в списке будут находиться в куче. –

+2

Почему вам все равно, где они хранятся? – Pubby

+1

Pubby, данные стека будут удалены в конце функции. – danihp

ответ

2

Где элементы списка хранятся? Стек? Heap?

Элементы списка хранятся в куче. Вы можете увидеть это после вашего отладчика вниз при вызове метода 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; 
} 

Вы код потерпит неудачу, как вы вернуть ссылку в свой список «Рес», который уже будет уничтожен в конце его объем, так что вы ссылка будет инвалид.

Проблема с первым решением может быть производительностью. Вы также можете сделать это без вызова конструктора копии, как это:

void func(list<int>& res) 
{ 
    res.push_back(10); 
} 

list<int> list_to_fill; 
func(list_to_fill); 

В этом случае нет никакого копирования, и это должно быть быстрее, так как есть только один создание списка.

+0

Можете ли вы расширить свое сообщение с помощью кода, чтобы записать конструктор? – danihp

+0

Вы неверны в вызове созданного экземпляра при возврате списка. Возвращенный список имеет ту же ссылку, что и внутри вызова функции из-за оптимизации компилятора. См. Мой ответ. – ComputerEngineer88

2

Где элементы списка хранятся? Стек? Heap?

Элементы, принадлежащие списку, обычно динамически распределяются так, что это будет в куче.

Эта функция может возвращать список?

Нет, он не может, вы объявили свою функцию с типом возврата типа void.

Как я могу сделать, чтобы эмпирически проверить, что значения находятся в стеке или куче?

Единственный способ быть уверенным в том, чтобы заглянуть в реализацию std :: list, чтобы увидеть, что он действительно делает. Но вам действительно не нужно заботиться об этом, так как это деталь реализации.

+0

Я знаю, что функция объявлена ​​как void, но если я объявляю функцию как список , могу ли я вернуть список с проблемами? – danihp

+1

Да, вы можете это сделать, но вам придется вернуть его по стоимости. Это может повлечь за собой дорогостоящую операцию копирования в зависимости от того, насколько сложно использовать ваш список и количество элементов в нем. Однако, если вы разрешаете использовать C++ 11, вы можете исключить копирование с помощью std :: move semantics. – greatwolf

+0

Возможно, можно узнать, находится ли он в куче или стеке с помощью внешнего инструмента? Или распечатать позицию указателя? – danihp

1

Если вы изменили подпись функции, чтобы вернуть список, вы действительно можете вернуть список; однако компилятор сделает копию , а копия - той, которая будет получена вызывающим абонентом.

Исходный список будет уничтожен в конце функции, но копия будет сделана до того, как это произойдет, чтобы вы вернули действительные данные. Если вы попытаетесь обмануть систему и вместо этого вернете указатель или ссылку, чтобы избежать копирования, тогда вы столкнетесь с неопределенным поведением.

Сам список будет находиться в стеке, но элементы, содержащиеся внутри, будут находиться в куче. Список будет содержать указатели, которые он управляет за кулисами, чтобы вам вообще не пришлось беспокоиться о хранилище.

+0

В любом случае, перемещение семантики позволит вернуть список по значению, не касаясь динамически распределенных узлов списка. –

+0

@KerrekSB, есть ли способ гарантировать семантику переноса или вы во власти оптимизации вашего компилятора? –

+0

Есть ли способ проверить это эмпирически? Адрес элемента печати? – danihp

1

Некоторые из них могут варьироваться в зависимости от прихоти компилятора и/или автора библиотеки.

Как сейчас, существует довольно хороший шанс, что компилятор обнаружит, что результат создания и заполнения списка никогда не используется, поэтому он исключит всю функцию как мертвый код. Чтобы этого не произошло, вы можете (например) вернуть список вместо того, чтобы просто уничтожить его в конце функции.

Это только добавляет новые возможности. Большинство компиляторов реализуют оптимизацию возвращаемого значения (RVO) и оптимизацию с наименьшими возвращаемыми значениями (NRVO).Это в основном означает, что когда/если вы возвращаете значение (например, ваш объект list) вместо того, чтобы создавать объект в стеке и копировать его в пункт назначения при его возврате, компилятор генерирует код для генерации объекта, в котором он происходит назначается после возвращения. В этом случае вместо создания локальной функции функция просто получит скрытый указатель на место, где будет записываться результат.

A std::list обычно выделяет пространство для хранения данных, используя std::allocate. Однако вы можете указать другой распределитель для его использования. В этом случае пространство можно было бы разместить практически в любом месте.

Хотя это чаще встречается с другими (вроде) контейнерами, такими как std::string, также возможно (по крайней мере в некоторых случаях) хранить как минимум небольшой объем данных в самом объекте и выделять пространство в куче только тогда, когда/если это переполнение. Для обеспечения безопасности исключений существуют некоторые ограничения, но в случае list<int> это не должно быть проблемой. Например, первая десятка int s может быть сохранена в самом объекте list, и только когда/если вы добавите больше этого, оно выделит место в куче.

+0

Большое спасибо за ваш ответ. Есть ли способ проверить, что L в функции и L в главном - это тот же список эмпирически? Печатать адрес адреса или адреса списка адресов? – danihp

+0

@ danihp: вы можете распечатывать адреса элементов в списке. Если они совпадают между функцией и main, это, по крайней мере, говорит вам, что * содержимое * этого списка не было скопировано во время возврата. У вас могут быть отдельные объекты списка, но это почти не имеет значения (объект 'list' обычно содержит немного больше, чем указатель или два). –

+0

nice Jerry, вы хотите добавить свой почтовый индекс для печати указателей? – danihp

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; 

} 
+0

Есть ли способ проверить, что L в функции и L в главном - это тот же список эмпирически? Печать списка или адреса элемента? – danihp

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; 
} 
+0

Спасибо за ваш ответ. – danihp

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) 

Вы можете дважды проверить это, напечатав & Рез в функции и & л вне функции.

Компилятор оптимизирован для передачи возвращаемых объектов без копирования, в противном случае каждый отдельный объект без указателя будет скопирован, что было бы безумным.

+0

Спасибо! я проверю – danihp

Смежные вопросы