User 13204940 Ответов: 3

Управление памятью с помощью std::vector<somepointer*>


Привет,

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

1. std::vector<Item*> items хранит указатели на элементы, которые должны быть удалены, чтобы освободить память, когда игра закончится - поскольку это переменная-член, сами элементы будут удалены, когда PlayerInventory будет удален, но что касается указателей, которые он содержит, я не уверен, как ими управлять.

2. в RemoveItemsOfType я создаю новый элемент с именем nullItem и устанавливаю элемент в заданной позиции на nullItem. Нужно ли мне вызвать 'delete items[slot];', прежде чем я сделаю это, чтобы освободить память, удерживаемую элементом, находящимся в данный момент там, прежде чем я заменю его nullItem?

#include "Item.h"

void Item::Init(ItemType type)
{
	this->type = type;
}

ItemType Item::GetType()
{
	return type;
}
#include "PlayerInventory.h"

PlayerInventory::PlayerInventory()
{
	for (int i = 0; i < slots; i++)
	{
		Item* item = new Item();
		item->Init(ItemType::NONE);
		items.push_back(item);
	}
}

void PlayerInventory::SetItem(int slot, Item* item)
{
	items[slot] = item;
}

Item* PlayerInventory::GetItem(int slot)
{
	return items[slot];
}

void PlayerInventory::RemoveItemsOfType(ItemType type)
{
	for (int i = 0; i < slots; ++i)
	{
		if (GetItem(i)->GetType() == type)
		{
			Item* nullItem = new Item();
			nullItem->Init(ItemType::NONE);
			SetItem(i, nullItem);
		}
	}
}


Что я уже пробовал:

Я очень много думаю о том что делать но не знаю ответа поэтому пришел сюда за советом :)

Richard MacCutchan

Почему вы кладете новый предмет в слот? Почему бы просто не изменить тип существующего? Но если вы должны сделать это по-своему, то вам нужно удалить существующий элемент, прежде чем перезаписывать запись.

[no name]

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

Когда PlayerInventory будет деконструирован, а член 'items' удален, нужно ли будет удалить каждый из указателей в 'items' в PlayerInventory::~PlayerInventory()?

Richard MacCutchan

В общем, да. Если вы создадите объект с помощью new тогда вы должны избавиться от него с помощью delete Где именно вы это сделаете, будет зависеть от структуры вашего приложения.

[no name]

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

Richard MacCutchan

Ну что ж, дела идут на поправку, сегодня у тебя четверка.

[no name]

ура! :>

3 Ответов

Рейтинг:
2

Rick York

Я обнаружил, что да, вам действительно нужно вручную удалять векторы указателей. Если вы используете Visual Studio и макрос DEBUG_NEW, то в противном случае вы увидите утечки памяти в отладчике. По крайней мере, я так думаю. Некоторое время назад я придумал эту маленькую шаблонную функцию, чтобы помочь в этой ситуации :

// delete a vector of objects created with new

template< typename T >
void DeleteVector( std::vector<T> & v )
{
    size_t count = v.size();
    for( size_t n = 0; n < count; ++n )
        delete( v[n] );
    v.clear();          // just to be sure
}
Если вы используете вариант malloc для выделения указателей, то вы можете сделать вариацию этой функции, которая вызывает free вместо delete, и поскольку free не является типизированной операцией, функция не должна быть шаблоном.


[no name]

Я обязательно хорошенько в этом разберусь. Было бы хорошей идеей, чтобы все мои классы включали a .h с #define new DEBUG_NEW внутри, чтобы я мог собрать все варианты использования " new " во всем проекте? Я чувствую, что это дало бы мне хорошее представление об утечках памяти, где бы они ни находились.

Я читал, что std::vector::clear() уничтожает и удаляет каждый элемент в векторе, поэтому я считаю, что простого вызова clear() без цикла будет достаточно

jeron1

"Я читал, что std::vector::clear() уничтожает и удаляет каждый элемент в векторе,"
Это, вероятно, относится к памяти, которую выделяет сам вектор (4 или 8 байт, необходимых для хранения ваших указателей), а не к объектам, на которые эти указатели указывают.

[no name]

Откуда cplusplus.com: "удаляет все элементы из вектора (которые уничтожаются), оставляя контейнер размером 0."

jeron1

Элементы в вашем векторе являются указателями. Каждая запись размером 4 или 8 байт удаляется/уничтожается, а не объекты, на которые они ссылаются. Возможно, установите точку останова деструктора на "элементе" и посмотрите, будет ли он вызван, когда ваш вектор будет уничтожен.

[no name]

Отличная идея, я об этом не подумал. На самом деле я использую Unreal Engine 4, который использует Visual Studio для разработки, поэтому я не верю, что есть способ установить точки останова, но я определенно могу вывести что-то на консоль внутри деструктора

jeron1

Никаких точек останова или возможности пошагового просмотра кода? Он должен быть где-то там, я не могу себе представить (и не рассматривал бы) никакого развития без него.

[no name]

Это очень недружелюбный двигатель. Кривая обучения наверняка оттолкнет большинство людей еще до того, как они начнут. Я дошел до того, что разозлился на его бесполезность, отсутствие примеров C++ с документацией или даже четкого объяснения, а также на то, что они, похоже, заботятся только об элементе blueprint, полностью игнорируя тех, кто хочет лучше контролировать и программировать на C++. Все очень сложно, у вас даже не может быть конструкторов с аргументами.

Задайте общий вопрос в google относительно unreal engine, и вы увидите почти только примеры чертежей.

Путь UE4-это единственный путь, в котором нет места привычкам каждого разработчика.

jeron1

Звучит не очень весело.

Рейтинг:
1

CPallini

Используйте умный указатель. Попробуйте, например

#include <iostream>
#include <vector>
#include <memory>
using namespace std;

class F
{
  int f;
public:
  F(int f):f(f) { cout<< "F(" << f << ") ctor called\n";}
  ~F() { cout << "F(" << f  << ") dtor called\n";}
};

int main()
{
  vector< F * > v;
  v.push_back(new F(1));

  vector < unique_ptr< F > > w;
  w.push_back( make_unique< F >(2) );
}

Выход:
F(1) ctor called
F(2) ctor called
F(2) dtor called


Рейтинг:
0

KarstenK

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

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

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

std::vector<Item> myList;


[no name]

Итак, в PlayerInventory у меня есть переменная-член std::vector<Item*> items.

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

jeron1

"в PlayerInventory у меня есть переменная-член std::vector<Item*> items...."
"Когда PlayerInventory будет уничтожен, элементы также будут автоматически удалены."
Кто-нибудь поправит меня, если я ошибаюсь, но мне это не кажется правильным. Я думаю, что если вы собираетесь "новые" объекты, вам нужно "удалить" их, вероятно, в деструкторе PlayInventory. Я согласен с Карстенком в этом вопросе в использовании типизированного вектора, тогда управление памятью находится вне ваших рук.

[no name]

Мне сказали, что переменные-члены, сам std::vector, будут удалены, когда класс будет уничтожен. Я не знаю, автоматически ли это удаляет указатели в векторе или нет, которые создаются с помощью "new".

вектор объявляется как таковой;

public:
        std::vector<Item*> items;

KarstenK

В C++ указатель werent не удалялся при удалении контейнера. Это происходит только с умными указателями.

Совет опытного разработчика: изучите и поймите методы, которые вы хотите использовать ;-)

[no name]

Я лучше всего понимаю, когда пытаюсь, вместо того чтобы читать книгу и не иметь ни малейшего представления о том, что на самом деле переводится в реальном проекте.