2013-05-14 4 views
1

Я новичок в C++, и я пытаюсь сделать небольшую игру. У меня есть этот класс с именем «UnitController», который хранит несколько экземпляров класса «Единица» на карте. Класс также имеет метод «getUnit», который должен возвращать один из сохраненных единиц.Получить экземпляр объекта с карты

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

Может ли кто-нибудь указать мне в правильном направлении?

#include "UnitController.h" 
#include "Unit.h" 

using namespace ci; 
using std::map; 

UnitController::UnitController() 
{ 
} 

void UnitController::addUnit(Vec2f position) 
{ 
    Unit mUnit = Unit(); 
    mUnit.setup(position); 
    Units.insert(std::pair<int,Unit>(Units.size()+1, mUnit)); 
} 

Unit UnitController::getUnit(int k) 
{ 
    Unit selectedUnit = Units[0]; 
    return selectedUnit; 
} 

void UnitController::update() 
{ 
    for(map<int,Unit>::iterator u = Units.begin(); u != Units.end(); ++u){ 
     u->second.update(); 
    } 
} 

void UnitController::draw() 
{ 
    for(map<int,Unit>::iterator u = Units.begin(); u != Units.end(); ++u){ 
     u->second.draw(); 
    } 
} 

ответ

3

Метод:

Unit UnitController::getUnit(int k) 
{ 
    Unit selectedUnit = Units[0]; 
    return selectedUnit; 
} 

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

Unit& UnitController::getUnit(int k) 
{ 
    return Units[k]; 
} 

Если запись ключа по k удаляется из map затем вызывающей стороны, имеет ссылку на Unit этой записи, теперь имеет свисающую ссылку, использование которой является неопределенным поведением. Там несколько вещей, чтобы рассмотреть, чтобы попытаться избежать этого:

  1. клиент UnitController Требует ли прямой доступ к Unit в map? Если нет, и клиенту требуется только обновить определенные атрибуты Unit, то измените интерфейс UnitController для поддержки обновлений Unit без предоставления прямого доступа клиента.
  2. Если клиент требует прямого доступа, рассмотрите возможность использования std::shared_ptr<Unit> вместо Unit для типа входного значения (и не в этом случае возвратитесь по ссылке). Это будет касаться проблемы с обвисшей ссылкой, но что это значит, что для вызывающего абонента есть доступ к Unit, который больше не находится в UnitController?

operator[] создаст запись в карте для ключа, который на данный момент не существует:

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

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

+0

Что делать, если к> = Units.size()? :-) – user1764961

+0

@ user1764961, его 'map', а не' vector' или 'array'. – hmjd

+0

точно. Я не думаю, что OP будет доволен этим поведением. – user1764961

0

Вы получите глубокую копию блока.

Рассмотрите возможность создания указателя подсчета ссылок (aka smart pointer), инкапсулирующего Единицу, и установите его для типа значения карты.

0

, конечно, вы получаете копию.считают это:

void UnitController::addUnit(Vec2f position) 
{ 
    Unit mUnit = Unit();  // you create local instance of Unit 
    mUnit.setup(position); 
    // you add it to the map - actually the copy is created and stored in map 
    Units.insert(std::pair<int,Unit>(Units.size()+1, mUnit)); 
    // mUnit is destroyed after the function exits 
    // your map would now contain a destroyed object if it wasn't a copy 
} 

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

Возможно, вы захотите использовать map<int,Unit*>, но тогда вам необходимо позаботиться о времени жизни исходных объектов. Вы не можете его уничтожить, потому что указатель укажет на уничтоженный объект. Поэтому правильным решением является использование shared_ptr<Unit>

Однако есть и другие проблемы с дизайном. Вы сохраняете текущий размер + 1 в качестве ключа карты, что означает, что вы используете его как индекс. Почему бы просто не использовать std::vector<shared_ptr<Unit>>?

2

В этом коде:

Unit UnitController::getUnit(int k) 
{ 
    Unit selectedUnit = Units[0]; 
    return selectedUnit; 
} 

вы возвращаетесь Unitпо значению, так что вы на самом деле получаете копию оригинального Unit.

(Заметим также, что вы, кажется, есть ошибка, так как вы используете 0 в качестве ключа, вместо параметра k ...)

Если вы хотите изменить оригинальныйUnit (т.е. один хранящийся в карта), вы можете вернуть по ссылке (Unit &):

Unit& UnitController::getUnit(int key) 
{ 
    return Units[k]; 
} 

в качестве примечания, вы можете упростить свой код вставки. Вместо того чтобы использовать std::map::insert() метод:

Units.insert(std::pair<int,Unit>(Units.size()+1, mUnit)); 

вы можете просто использовать std::map::operator[] перегрузки:

Units[ ...the key here... ] = mUnit; 
Смежные вопросы