Поведение контейнеров C ++ - PullRequest
6 голосов
/ 03 февраля 2010

Мой вопрос прост. Когда я использую контейнеры STL, они копируют значение, которое я храню там (используя конструктор копирования) или нет? Что если я дам им массив символов (char *) вместо экземпляра строки? Как они себя ведут? Гарантируется, что информация будет храниться в куче, а не в системном стеке?

Спасибо за ответы.

Ответы [ 6 ]

7 голосов
/ 03 февраля 2010

Значения в контейнерах STL сохраняются по значению. Если у вас есть такой вектор:

class BigObject
{
...
};

vector<BigObject> myObjs;
myObjs.push_back(obj1);
myObjs.push_back(obj2);
...

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

То же самое верно, когда у вас есть вектор указателей, например vector<char*> - но разница здесь в том, что значение, которое копирует, является указателем, а не строкой, на которую он указывает. Так что если у вас есть:

vector<char*> myStrings;
char* str = new char[256];     // suppose str points to mem location 0x1234 here
sprintf(str, "Hello, buffer");
myStrings.push_back(str);
delete [] str;

... вектор получит копию указателя. Указатель, который он получает, будет иметь то же значение (0x1234), и, поскольку вы delete d указатель после нажатия на указатель, ваш вектор содержит дикий указатель, и ваш код в конечном итоге потерпит крах (скорее, чем позже, надеюсь).

Чего, кстати, можно было бы избежать, если бы вместо char * s вы использовали строки:

typedef vector<string> strings;
strings myStrings;
myStrings.push_back("Hello, buffer");
5 голосов
/ 03 февраля 2010

Они копируют значение. Большинство (все?) Контейнеров требуют, чтобы были определены конструктор копирования и оператор присваивания.

Если вы дадите им char*, они копируют указатель, а не значение, на которое указывает. Тогда вы несете ответственность за то, чтобы убедиться, что сама строка не уничтожена во время ее использования и уничтожена, когда в ней больше нет необходимости.

3 голосов
/ 03 февраля 2010

Они всегда делают копии.Если вы создадите vector<char *>, он скопирует char*, который вы вставили в него.Однако он не будет копировать строку символов, на которую указывает указатель.Если эта строка была выделена динамически, возможно, у вас есть утечка, поэтому vector<string> обычно бесконечно предпочтительнее.

1 голос
/ 03 февраля 2010

Вы уже получили ряд ответов, указывающих на то, что когда вы помещаете данные в контейнер, они помещаются туда по значению, поэтому в контейнер попадает копия ваших исходных данных.

Однако есть одно исключение из этого правила: std::list имеет член splice, который позволяет вставлять часть (или весь) одного списка в другой список. В этом случае данные обычно перемещаются, а не копируются - по крайней мере, обычно объединение выполняется с помощью указателей, поэтому узлы, которые были в одном списке, переносятся в другой список.

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

1 голос
/ 03 февраля 2010

Они всегда копируются, когда вы кладете их. Если вы дадите им указатель, то они скопируют указатель. Данные, на которые указывает указатель, не затрагиваются. Обратите внимание, что не рекомендуется помещать сырые указатели в контейнеры stl, потому что их легко потерять и потерять память. Контейнеры STL не были разработаны, чтобы помочь вам с необработанными указателями. Если вы хотите использовать указатели в контейнерах STL, рассмотрите shared_ptr из boost, чтобы обернуть их.

0 голосов
/ 03 февраля 2010

Более того, если вам не нужны накладные расходы на копирование целых объектов и вам не нравятся головные боли, связанные с использованием указателей, вы можете использовать boost :: shared_ptr внутри вектора, чтобы гарантировать, что память, выделенная динамически, будет освобождена только когда на него больше не ссылаются.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...