从地图中检索对象实例

时间:2013-05-14 10:47:41

标签: c++

我是C ++的新手,我正在尝试制作一款小游戏。我有一个名为“UnitController”的类,它在地图中存储类“Unit”的多个实例。该类还有一个方法“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();
    }
}

4 个答案:

答案 0 :(得分: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是什么意思?
  3. operator[]将在地图中为当前不存在的密钥创建一个条目:

      

    使用key作为键和默认构造的映射值,将新元素插入容器,并返回对新构造的映射值的引用。如果具有键键的元素已存在,则不执行插入,并返回对其映射值的引用。

    如果您不希望出现此行为,请使用find()并确定如果密钥k的条目不存在,应采取的操作。

答案 1 :(得分: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;

答案 2 :(得分:0)

您确实会收到一份深刻的单位副本。

考虑创建一个封装Unit的引用计数指针(也称为智能指针),并将其设置为map的值类型。

答案 3 :(得分: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>>