2009-11-25 3 views
1

Я пытаюсь создать по существу два вектора объектов, где некоторые объекты вводятся в оба списка, а некоторые просто вводятся в один. Первая проблема, которую я обнаружил, заключалась в том, что когда я использовал push_back(), чтобы добавить объект в оба списка, объект был скопирован так, что когда я изменил его из одного списка, объект не изменился в другом. Чтобы обойти это, я попытался создать список указателей на объекты как один из списков. Однако, когда я обратился к указателю позже, данные, казалось, были повреждены, значения элементов данных были неправильными. Вот некоторые фрагменты моего кода:C++ vector указателей объектов класса

Определение векторов:

vector<AbsorbMesh> meshList; 
vector<AbsorbMesh*> absorbList; 

... Добавление объекта как:

AbsorbMesh nurbsMesh = nurbs.CreateMesh(uStride, vStride); 

// Add to the absorption list 
absorbList.push_back(&nurbsMesh); 
// Store the mesh in the scene list 
meshList.push_back(nurbsMesh); 

Доступ к объекту:

if (absorbList.size() > 0) 
{ 
float receivedPower = absorbList[0]->receivedPower; 
} 

Что я делаю неправильно?

+0

Почему meshlist хранит * копию * реальных объектов? Если вы хотите сохранить тот же объект в обоих векторах, то оба должны быть указателями – Naveen

+0

Какова ваша фактическая цель здесь (кроме 2 полуподобных списков)? Хранение указателей в контейнерах STL обычно приводит к плохим вещам. –

+0

@Adam Почему? Только потому, что указатели не могут быть освобождены? На мой взгляд, хорошо хранить указатели (или интеллектуальные указатели) внутри контейнеров STL, так как таким образом вы не платите за создание копии объектов при изменении размера контейнера. –

ответ

8

Есть некоторые недостающие подробности, но при догадках.

nurbsMesh выходит из сферы действия между push_back и absorbList[0]->receivedPower.

Итак, теперь ваш вектор указателей содержит указатель на объект, который больше не существует.

Попробуйте добавить конструктор копирования к вашему классу AbsorbMesh и добавьте его в свой вектор следующим образом.

absorbList.push_back(new AbsorbMesh(nurbsMesh)); 
meshList.push_back(nurbsMesh); 

не забудьте удалить объекты в absorbList, как этот

for(vector<AbsorbMesh*>::iterator it = absorbList.begin(); it != absorbList.end(); it++) { 
    delete it; 
    } 

Или хранить общий указатель в ваш вектор вместо голого указателя. Если вам интересно, у Boost есть хорошая совместная реализация указателя. См. Документацию here

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

Используя исходные требования (обновление элемента в один вектор влияет на элементы в другом векторе, вот как я бы сделал это с повышающим общим указателем. (ВНИМАНИЕ, непроверенный код)

vector<boost::shared_ptr<AbsorbMesh> > meshList; 
vector<boost::shared_ptr<AbsorbMesh> > absorbList; 

boost::shared_ptr<AbsorbMesh> nurb = new AbsorbMesh(nurbs.CreateMesh(uStride, vStride)); 

meshList.push_back(nurb); 
absorbList.push_back(nurb); 

... 
... 

if (absorbList.size() > 0) 
{ 
    float receivedPower = absorbList[0].get()->receivedPower; 
} 
+0

@ flyfishr64, эта link не работает. Я предполагаю, что вы ссылаетесь на какой-то общий указатель? Обычно хорошая идея, но если у ОП возникают проблемы с определением области обзора, обычно лучше начинать с простых вещей. – Glen

+0

Вместо того чтобы иметь дело с удалением ваших объектов, используйте RAII (инициализация ресурсов) и заверните объекты в интеллектуальный указатель перед добавлением их в вектор. http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization –

+0

Просто заметили, что ссылка не сработала ... как можно вставить ссылку в комментарий здесь? –

6

Вы храните адрес объекта, выделенного в стеке. Объект nurbsMesh уничтожается, как только заканчивается ваш метод, который заканчивается push_back(). Если вы попытаетесь получить доступ к этому указателю, то объект уже уничтожен и содержит мусор. Вам нужно сохранить объект, который остается даже после того, как функция выходит за рамки. Для этого выделите память для объекта из кучи, используя new. Но для каждого нового вы должны иметь соответствующий delete. Но в вашем случае у вас будут проблемы с удалением, поскольку вы нажимаете один и тот же указатель на два вектора. Чтобы решить эту проблему, вам понадобится некоторый механизм подсчета ссылок на тип.

1

absorbList.push_back(&nurbsMesh); является неправильный

absorbList сохранить указатель на локальный объект.Когда nurbMesh уничтожен, вы не можете писать absorbList[0]->

2

Объект удален при попытке получить указатель от вектора.

Try сделать

vector.push_back(new Object); 
2

После того, как вы устранили проблему, что другие упомянутую (хранящий указатель на объект, который находится на стеке), вы собираетесь работать в другой вопрос. A vector может перераспределять, что приводит к перемещению его содержимого в другое место.

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

+0

Итак, если бы я сделал то, что предложил Адам: meshList.push_back (nurbsMesh); absorbList.push_back (meshList.back()); У меня были бы проблемы, если meshList перераспределился? Какие события могут вызвать это? Вставка, удаление ... другие? – Nigel

+0

С этим кодом нет проблем. Если ваши векторы хранят указатели, то сам указатель дублируется. Если они хранят сами объекты, они дублируются; но тогда, конечно, модификации одного не появляются в другом. Я думаю, что любая модификация 'vector' может привести к перераспределению. На практике сокращение вектора не приводит к сокращению его емкости (в libC++), но я не уверен, стандартизировано ли это поведение. – Thomas

1

Однако когда доступ указатель позже данные, казалось, испорчены

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

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

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

1

Есть несколько вещей неправильно с вашим примером

AbsorbMesh nurbsMesh = nurbs.CreateMesh(uStride, vStride); 

Этот объект выделяется в стеке. Это чисто локальный объект. Этот объект будет уничтожен, когда вы достигнете конца текущего блока, окруженного {}.

absorbList.push_back(&nurbsMesh); 

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

meshList.push_back(nurbsMesh) 

И это копирует совершенно новый объект на вектор.

Непосредственно проталкивать объект по вектору сначала, а затем нажимать указатель на объект на вектор, используя absorbList.push_back(&meshList.back()), потому что vector::push_back перераспределит весь вектор, недействительным всех указателей.

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

В качестве альтернативы, создайте объекты в куче, используя new AbsorbMesh(), но обязательно вызовите delete на каждый созданный таким образом указатель. В противном случае у вас есть утечка памяти.

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

1

Во-первых, как отмечают остальные, вы не можете выделять объекты в стеке (то есть, кроме new или что-то подобное) и иметь их после выхода из области.

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

В-третьих, auto_ptr<> просто не работает в контейнерах STL, так как auto_ptrs не может быть скопирован.

Указатели на отдельные выделенные объекты работают, но удаление их в нужное время сложно.

Что, вероятно, будет работать лучше всего shared_ptr<>. Сделайте каждый вектор vector<shared_ptr<AbsorbMesh> >, выделите через new, и при небольшой стоимости в производительности вы избегаете много хлопот.

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