从映射中检索对象实例

Retrieve object instance from map

本文关键字:对象 实例 检索 映射      更新时间:2023-10-16

我是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();
    }
}

方法:

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

返回具有索引 0 的元素的副本(可能是默认的((您的意思是忽略k吗?如果您希望避免返回副本,则返回对索引 0 处元素的引用,而不是对局部变量 selectedUnit 的引用:

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

如果从map中删除由 k 键控的条目,则引用该条目Unit的调用方现在具有悬空引用,其使用是未定义的行为。要避免这种情况,需要考虑以下几点:

  1. UnitController客户端是否需要直接访问map中的Unit?如果不是,并且客户端只需要更新Unit的某些属性,则修改UnitController接口以支持对Unit的更新,而无需提供客户端直接访问。
  2. 如果客户端确实需要直接访问,请考虑对条目值类型使用 std::shared_ptr<Unit> 而不是 Unit(在这种情况下不要通过引用返回(。这将解决悬而未决的引用问题,但是调用方可以访问不再在UnitController中的Unit意味着什么?

operator[]将在映射中为当前不存在的键创建一个条目:

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

如果您不希望出现此行为,请使用 find() 并决定在不存在键k条目时要执行的操作。

在此代码中:

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;

您确实会收到一份 Unit 的深度副本。

考虑创建一个封装单位的引用计数指针(又名智能指针(,并将其设置为映射的值类型。

当然你会

得到副本。 考虑一下:

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>>呢?