2010-08-23 2 views
3

Я опытный кодер, но все еще относительно новый для STL, и только пришли на эту проблему:Жизненный цикл объектов, передаваемых со ссылкой на STL контейнеры

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

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

с использованием пространства имен std;

queue<string> strQueue; 

const char *genStr(int i) 
{ 
    ostringstream os; 
    os << "The number i is " << i; 
    strQueue.push(os.str()); 
    return strQueue.back().data(); 
} 

void useStr() 
{ 
    while(!strQueue.empty()) 
    { 
     cout << strQueue.front() << endl; 
     strQueue.pop(); 
    } 
} 

int main(int argc, char **argv) 
{ 
    for(int i = 0; i < 40; i++) 
    { 
     printf("Retval is: %s\n", genStr(i)); 
    } 
    useStr(); 

    return 0; 
} 

Как струны выходит из области видимости, когда genStr() выходит, я бы ожидать, что Printf на только выход «RetVal является:», или, по крайней мере, для вызова useStr(), чтобы дать неопределенные результаты , так как память прерывалась повторными выделениями из дополнительных вызовов, но оба они возвращают соответствующие сохраненные строки.

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

Благодаря

+2

Я бы хотел добавить, не возвращать const char *. Используйте std :: string и std :: cout. Для этого они нужны. Мне кажется, что вы находитесь в середине конвертации с C на C++. Строковые классы не должны делать ничего такого, как обнуление их первого символа. Это будет C-ism. В C++ безопасность намного опережает это. – Puppy

+0

Спасибо за совет. Обычно я использую std :: string и std :: cout (как вы можете видеть в useStr), что printf и возвращающий const char * были всего лишь запаникованной попыткой доказать себе, что базовые данные были все еще хорошо, и я мог бы положиться на это. –

ответ

8

Насколько мне известно, STL контейнеры не предназначены для копирования объектов, которые они содержат

Хорошо, давайте остановимся прямо там. Контейнеры STL do часто копируют их содержимое. Они копируют их, когда они вставлены, они копируют их, когда контейнер изменяется либо автоматически, либо явно, и копирует их, когда сам контейнер копируется. Много копий.

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

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

+0

Спасибо. У меня сложилось впечатление, что контейнеры STL хранятся по ссылке вместо копирования данных из того факта, что все методы хранения берут ссылки. Оглядываясь назад, это было плохое предположение, но в моей защите было довольно поздно, хе-хе. Что касается ссылки, я предполагал, что компилятор неявно передавал ссылку на очередь, когда я вызываю «strQueue.push_back (os.str(). Data()» - поскольку push_back принимает только ссылки, я все еще верю это будет иметь место, но я был бы признателен за исправление, если я ошибаюсь –

+0

В C++ распространенная практика заключается в том, что функции и методы принимают параметры объекта (т. е. вещи, отличные от int, floats, chars) с помощью ссылки * const *. Это позволяет избежать копирования при вызове функции, но это не означает (и фактически почти никогда) означает, что функция будет привязана к ссылке на этот объект. В контейнерной ситуации объект-контейнер будет принимать объект к вставьте ссылку const, а затем скопируйте ее в постоянное хранилище, в результате чего одна копия вместо двух копий (одна в параметр, а затем другая в хранилище). –

0

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

+0

То, что вы получаете, не обязательно является копией, если вы не скопируете ее самостоятельно. 'int & i = vec.front(); // ссылка на первый элемент – UncleBens

0

Все коллекции «STL» (я ненавижу этот термин) хранят копии переданных им объектов, поэтому время жизни объекта в коллекции полностью не зависит от исходного объекта. При нормальных обстоятельствах копия объекта коллекции останется действительной до тех пор, пока вы не удалите ее из коллекции или не уничтожьте коллекцию.

+0

Из любопытства, почему вы ненавидите термин «STL»? –

+0

@Pedro: Я не ненавидел его, но это своего рода плохое имя. Слова standard, template и library не дают вам никаких указаний на что-либо полезное или релевантное в библиотеке, и, похоже, что шаблоны - это некоторые специализированные технологии, а не то, что вы всегда должны использовать в своих приложениях на C++. –

+1

@Pedro: потому что разные люди имеют в виду такие разные вещи, что это приводит к множеству недоразумений. Даже то, что означает слова «STL», варьируется. Некоторые люди означают «STandard Library», другую «Стандартную библиотеку шаблонов» и т. Д. Даже тогда, если вы имеете в виду стандартную библиотеку шаблонов, вы имеете в виду библиотеку, которая была представлена ​​комитету ~ 15 лет назад, части этой библиотеки, которые были приняты в стандарт, или текущую библиотеку под этим именем? Если вы имеете в виду стандартную библиотеку, какие части? –

5

STL контейнеры не предназначены для копирования объектов, которые они содержат

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

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

Вот еще одно недоразумение прояснится:

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

C++ не стремится когда-либо делать подобные вещи. Это будет скрытая стоимость, а C++ ненавидит скрытые затраты :) Дескриптор строки не коснется памяти, потому что, как только деструктор вышел, объект больше не существует. Доступ к нему - неопределенное поведение, поэтому реализация C++ будет делать все, что быстрей и наименее расточительно в четко определенном коде.

+0

+1 для лучшего ответа, но одна вещь с формулировкой: «объекты, выходящие из области видимости», больше не являются объектами, просто необработанная память ... – hjhill

+0

@hjhill: Что происходит сначала - фигурная скобка или деструктор? Если дерево падает в лес ... Я исправлю формулировку, чтобы не было двусмысленности. :) –

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