2012-06-19 2 views
0

Я ищу способ вставки нескольких объектов типа A внутри объекта контейнера, без копирования каждого объекта A во время установки. Один из способов - передать объекты A по ссылке на контейнер, но, к сожалению, насколько я читал, контейнеры STL принимают только объекты передачи по значениям для вставок (по многим причинам). Обычно это не проблема, но в моем случае я НЕ хочу, чтобы конструктор копирования был вызван, и исходный объект был уничтожен, потому что A является оберткой для библиотеки C, с некоторыми указателями на C-стиль для структур внутри, которое будет удалено вместе с исходным объектом ...Хранение ссылок на объекты в простом контейнере

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

Другой подход заключается в том, чтобы хранить указатели до A внутри контейнера, но поскольку у меня нет большого количества знаний по этому вопросу, что было бы правильным способом вставить указатели на объекты в контейнере STL? Например это:

std::vector<A *> myVector; 
for (unsigned int i = 0; i < n; ++i) 
{ 
    A *myObj = new myObj(); 
    myVector.pushBack(myObj); 
} 

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

Кроме того, некоторые люди предлагают использовать такие вещи, как shared_ptr или auto_ptr или unique_ptr, но меня путают со множеством опций. Какой из них был бы лучшим выбором для моего сценария?

+4

определенно * не * использовать 'auto_ptr' –

+1

Либо' повысить :: scoped_ptr' или 'станд :: unique_ptr' будет работать -' станд :: вектор <зЬй :: unique_ptr > ' –

ответ

4

В C++ 11, вы можете создать элементы контейнера в месте, используя emplace функции, избегая затраты и хлопоты управления контейнером указателей на выделенные объекты:

std::vector<A> myVector; 
for (unsigned int i = 0; i < n; ++i) 
{ 
    myVector.emplace_back(); 
} 

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

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

UPDATE: Так как это не будет работать на вашем компиляторе, лучший вариант, вероятно, std::unique_ptr - что не имеет накладных расходов по сравнению с обычным указателем, будет автоматически удалять объекты, когда стираются из вектора, и позволяет если хотите, переместите право собственности из вектора.

Если это не доступно, то std::shared_ptr (или std::tr1::shared_ptr или boost::shared_ptr, если это не доступно) также даст вам автоматическое удаление, для (возможно, небольшой) стоимости в эффективности.

Что бы вы ни делали, не Пробуйте хранить std::auto_ptr в стандартном контейнере. Это разрушительное поведение при копировании позволяет легко случайно удалить объекты, когда вы этого не ожидаете.

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

+0

Это действительно опрятный Майк, но, к сожалению, я использую VS 2010, который на данный момент предоставляет просто избыточный emplace_back, как указано здесь http://stackoverflow.com/a/4306581/1174378, поэтому кажется, что я не могу чтобы использовать эту функцию, потому что конструктор объекта принимает некоторые аргументы :( –

+0

@MihaiTodor: Это позор. Я обновил ответ с некоторыми альтернативами. –

+0

Хорошо, аккуратно! Но если я использую 'std :: unique_ptr' или' std :: shared_ptr', автоматически ли освобождается память, как я могу избавиться от вектора? –

5

Вы можете использовать boost или stdreference_wrapper.

#include <boost/ref.hpp> 
#include <vector> 

struct A {}; 


int main() 
{ 
    A a, b, c, d; 
    std::vector< boost::reference_wrapper<A> > v; 
    v.push_back(boost::ref(a)); v.push_back(boost::ref(b)); 
    v.push_back(boost::ref(c)); v.push_back(boost::ref(d)); 
    return 0; 
} 

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

int main() 
{ 
    std::vector< boost::reference_wrapper<A> > v; 
    { 
    A a, b, c, d; 
    v.push_back(boost::ref(a)); v.push_back(boost::ref(b)); 
    v.push_back(boost::ref(c)); v.push_back(boost::ref(d)); 
    // a, b, c, d get destroyed by the end of the scope 
    } 
    // now you have a vector full of dangling references, which is a very bad situation 
    return 0; 
} 

Если вам необходимо обрабатывать такие ситуации, вам нужен умный указатель.

Умные указатели также являются опцией, но важно знать, какой из них использовать. Если ваши данные фактически разделены, используйте shared_ptr, если в контейнере используется данные unique_ptr.

В любом случае, я не вижу, что изменит оберточная часть A. Если он содержит указатели внутри и подчиняется rule of three, ничто не может пойти не так. Деструктор позаботится о чистке. Это типичный способ обработки ресурсов в C++: получить их, когда ваш объект инициализирован, удалить их, когда срок жизни вашего объекта заканчивается.

Если вы просто хотите избежать накладных расходов на строительство и удаление, вы можете использовать vector::emplace_back.

+0

'std :: reference_wrapper', кажется, лучший выбор, потому что VS2010 не обладает надлежащей функциональностью' emplace_back'. Итак, если я буду использовать 'std :: reference_wrapper', мне все же нужно позаботиться об освобождении памяти вручную или все это сделано за капотом? –

+0

@MihaiTodor Будьте осторожны. Вам всегда нужно знать время жизни ваших объектов при использовании ссылочной обертки. Я добавлю пример. – pmr

+0

Итак, скажем, я пропустил 'std :: reference_wrapper' и вместо этого использовал' std :: vector > v; '. Будет ли проблема, описанная выше, все еще иметь место? Я не уверен, что понимаю, как это работает. Я имею в виду, что я полностью осознаю, что 'A, b, c, d;' будут уничтожены в конце блока и, таким образом, испортит мой вектор, что не то, что я хочу ... –

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