作为一个业余爱好项目,我希望改进开源运输游戏Simutrans Extended中的调试设施。
在游戏中,车队(公共汽车、火车等)是
convoy_t
类的实例。他们有一个成员变量uint ID
。每次处理一个车队时,网络代码都会将其 ID
的值添加到总数中。如果客户端和服务器的总数存在差异,它们就会不同步。计算客户端和服务器总数之间的差异可以得出导致不同步的车队的 ID 号(我们称之为 uint problematic_convoy
)。我想仅使用 problematic_convoy
来识别该 convoy 对象(这样我就可以将其传递给各种其他函数以获取更多信息等)。
正如您所期望的,
convoy_t
有一个公共成员函数get_ID()
,因此一旦我们有一个实例或指向它的指针,我们就可以检查任何实例的ID
。我考虑循环遍历类的所有实例来检查是否get.ID() == problematic_convoy
。但我只了解了 self.getID()
和 *mypointer.getID()
形式的示例;在这种情况下,我还没有可以与 operator.
一起使用的对象或指针。我怀疑我完全不知道解决整个问题的正确方法。
由于这是一个学习者的问题,我欢迎一个指向现有答案的“指针”(双关语),即我需要研究的 C++ 领域来做到这一点,例如相关语言功能的名称或相关LearnCPP.com 上的课程(我正在学习)。
我发现了这个类似的问题,但它是针对 Objective-C 的,而不是针对 C++ 的。答案推荐了工厂和单例模式,但它们似乎是用于实例化新对象,而不是访问大型代码库中的现有对象,并且单例模式似乎是有争议的。
编辑: 感谢 @drescherjm 和 @Ted Lyngmo 的有用评论,我想我现在知道了正确的方向。有一个类
convoihandle_t
,基于模板 quickstone_t,这是一个墓碑表。它看起来不像我以前遇到过的任何数组,但根据您的评论,它一定是一个我可以迭代的数组。那一定是我需要研究和理解的代码。谢谢!
一般来说,没有办法通过了解 C++ 类实例来找到 其数据成员之一的值,有两个原因:
数据成员值可能不唯一。
即使是这样,也没有语言或语言提供任何便利 用于在类的所有实例中搜索的标准库。
解决这个问题最典型的方法是构建某种 预先包含您要搜索的所有实例的容器 通过。
例如,如果存在一个创建对象的现有函数:
convoy_t *makeConvoy(...)
{
convoy_t *convoy = new convoy_t(...);
// maybe do other things
return convoy;
}
然后您可以修改此代码以通过以下方式跟踪所有对象 首先创建一个容器,例如
std::map
。为了
为了简单起见,我们将在文件范围内实现它,但可能有更好的地方
将其放入实际程序中:
#include <map> // std::map
#include <utility> // std::make_pair
// Map from convoy ID to a pointer to the instance.
std::map< int, convoy_t* > convoyMap;
然后,在
makeConvoy
中的 return
之前,添加插入它的代码
进入地图:
// This uses the "traditional" insertion syntax. See notes below.
convoyMap.insert(std::make_pair(
convoy->getID(), // key
convoy)); // value
at
绘制地图
方法:
convoy_t *convoy = convoyMap.at(ID);
一些注意事项:
您需要确保 ID 是唯一的。如果不是,那么当你尝试时 要将具有相同 ID 的第二个对象添加到地图中,
insert
调用不会执行任何操作(有关详细信息,请参阅链接参考)。
at
方法会抛出
std::out_of_range
如果未找到 ID。你应该编写一个异常处理程序来处理
有这种可能性。
对
insert
的调用也可以使用更紧凑的方式编写
利用初始化列表的符号,引入
C++11:convoyMap.insert({convoy->getID(), convoy});
。
如果一个物体是 已删除, 那么它也必须从地图上删除。否则,指针在 地图将“悬挂”,这意味着它指向记忆 随后被重新用于其他目的,导致各种 问题。
如果对象的 ID 发生更改,则必须将其从地图中删除并使用新 ID 重新插入。映射本身并不知道用作键的 ID 与数据成员值相同。
这是演示上述技术的完整程序:
// convoy.cc
// Demonstrate finding objects by ID.
#include <cassert> // assert
#include <map> // std::map
#include <utility> // std::make_pair
int nextConvoyID = 1;
class convoy_t {
public: // instance data
int m_id;
int m_otherData;
public: // methods
convoy_t(int id, int otherData)
: m_id(id), m_otherData(otherData) {}
int getID() const { return m_id; }
};
// Map from convoy ID to a pointer to the instance.
std::map< int, convoy_t* > convoyMap;
convoy_t *makeConvoy(int otherData)
{
int id = nextConvoyID++;
convoy_t *convoy = new convoy_t(id, otherData);
if (false) {
// Traditional syntax.
convoyMap.insert(std::make_pair(
convoy->getID(), // key
convoy)); // value
}
else {
// More compact notation using an initializer list.
convoyMap.insert({convoy->getID(), convoy});
}
return convoy;
}
int main()
{
// Make some convoys.
convoy_t *c1 = makeConvoy(11);
convoy_t *c2 = makeConvoy(12);
convoy_t *c3 = makeConvoy(13);
// Get one of the IDs.
int id = c2->getID();
// Amnesia strikes! Where did 'id' come from?
convoy_t *convoy = convoyMap.at(id);
// Ah, there it is.
assert(convoy == c2);
return 0;
}
// EOF
您说您在 Simutrans Extended 工作。我什么都不知道 关于它,但正如评论中指出的,它似乎有一个
world
可以是的对象
穿越:
// loop through all convoys
for (vector_tpl<convoihandle_t>::const_iterator i = world->convoys().begin(), end = world->convoys().end(); i != end; i++)
{
current_convoy = *i;
// only consider lineless convoys which support this compartment's goods catetory
if ( !current_convoy->get_line().is_bound() && current_convoy->get_goods_catg_index().is_contained(catg) )
{
temp_linkage.convoy = current_convoy;
linkages->append(temp_linkage);
transport_index_map[ 65536u + current_convoy.get_id() ] = linkages->get_count();
}
}
循环遍历所有车队以找到具有特定 ID 的车队是 效率不是很高,但可能足以满足您正在做的事情。可能还存在已经存在的运输地图;就像我说的,我不知道那个程序。