在 C++ 中存储数据,如 python 中的字典

Posted

技术标签:

【中文标题】在 C++ 中存储数据,如 python 中的字典【英文标题】:Storig data in C++ like dictionaries in python 【发布时间】:2020-04-26 18:30:59 【问题描述】:

我目前正在学习 c++,并且正在制作某种迷你游戏,其中你有村庄,这个村庄的建筑物,建筑物产生资源等。 首先我用 python 制作它,但决定开始用 c++ 制作它。 Python 有字典,对我来说效果很好。例如。

    BUILDINGS=  'Sawmill':  'buildingReq': 'wood':10,'iron':10,
                    'production':'wood':1,
                    'productionReq':'iron':1,
                    'upgrade':'Big Sawmil',
                    'buildingTime':5,
                 'Smith':  ......

然后我有:

 class Building():
   def __init__(self, buildingtype= '', x = 0, y = 0):
      self.buildingReq= constants.BUILDINGS[buildingtype]['buildingReq'] 
      self.production= constants.BUILDINGS[buildingtype]['production'] 
      self.productionReq= constants.BUILDINGS[buildingtype]['productionReq']


def work(self, village):
    for key in self.productionReq.keys():
        village.resource[key] = village.resource.get(key) - self.productionReq.get(key)
    for key in self.production.keys():
        village.resource[key] = village.resource.get(key) + self.production.get(key)

在创建对象期间,我具有搜索我想要构建的建筑物及其所有参数的功能。如您所见,这本词典中有不同的类型。在 C++ 中,我必须做 3 张地图,一张用于不同的数据类型集,一张用于构建需求地图(字符串构建,地图(字符串 buildingReq,地图(字符串木 int 10))),一张用于构建时间地图等

现在在 c++ 中我有这样的东西

 std::map < std::string, std::map < std::string, std::map < std::string, int>>> buildingInfo= 
"Sawmill", "buildingReq", "wood",15,"iron",15, "production",  "wood",10,"iron",10,
"Smith", "buildingReq", "wood",10,"iron",10, "production",  "wood",10,"iron",10,
"Big Sawmill", "buildingReq", "wood",10,"iron",10, "production",  "wood",10,"iron",10,
"Big Smith", "buildingReq", "wood",10,"iron",10, "production",  "wood",10,"iron",10

;

  std::map <std::string, int> buildingTime= 
"Sawmill", 3,
"Smith", 3,
"Big Sawmill", 3,
"Big Smith", 3

;

   std::map <std::string, std::string> upgrades= 
"Sawmill", "Big Sawmill",
"Smith", "Big Smith"

;

   std::map<std::string, int> workersNum= 
"Sawmmill",  10,
"Smith", 10


 class Building  
    public:
std::string buildingType;
std::map<std::string, int> production;
std::map<std::string, int> buildingReq;
 ;

他们通过

提取这些数据
  for (auto x : buildingInfo) 
    if (x.first == buildingType) 
        for (auto y : x.second) 
            if (y.first == "buildingReq") 
                this->buildingReq.insert(y.second.begin(), y.second.end());
            
            if (y.first == "production") 
                this->production.insert(y.second.begin(), y.second.end());
            
        break;
    

也许最大的问题不是提取数据,尽管可能是这样,而是以这种方式存储它。在 python 中我有一本字典,但在 c++ 中我有 4 个地图。 我怎么能在 C++ 中做一些类似的事情?还是有更好的方法?

【问题讨论】:

为您的建筑考虑一个真正的类,其中每个字段(需求、生产、升级等)都根据存储在这些字段中的数据类型进行强类型化。 您需要再解释一下您的确切用例,并包含您定义的类的示例。 @nanofarad 我的课堂上有这些字段,我需要的是方便的方式来存储数据和提取有关这些建筑物的数据。在 python 中,我在构造函数 self.buildingReq= constants.BUILDINGS[buildingtype]['buildingReq'] self.production= constants.BUILDINGS[buildingtype]['production'] self.productionReq= constants.BUILDINGS[buildingtype][ 'productionReq'] 你可以拥有持有这些常量的类;它们可以加载到内存中,例如在游戏启动时。除此之外,您的问题还不够清楚,无法提供更详细的见解 别担心,我会看看,看看我能想出什么。乍一看,为什么buildingInfo 不只是一个std::map&lt;std::string, Building&gt;,每个Building 都包含描述它的数据?我建议从这个问题中退后一步,专注于编写一个好的 Building 类,它具有有用的构造函数、方法等。之后,您可以研究如何有效地使用该类。 【参考方案1】:

编程语言之间的一个很大区别是它们的类型系统有多严格。 Python 的类型系统非常松散,而 C++ 则非常严格。在编写 C++ 时,您应该接受这一点,并在适当的地方创建显式类型。

正如 nanofarad 所提到的,您应该创建一个代表建筑物的 class。无需使用另一个std::map 来存储建筑物的属性,您只需为它们提供显式成员变量,每个变量都具有该属性的适当类型。下面是它的样子:

class BuildingInfo 
public:
    std::map<std::string, int> buildingReq;
    std::map<std::string, int> production;
    std::map<std::string, int> productionReq;
    std::string upgrade;
    int buildingTime;
;

然后您可以在编译时创建预定义的建筑类型数组,如下所示:

const std::map<std::string, BuildingInfo> buildings = 
    "Sawmill",
        
            /* buildingReq */    "wood", 15, "iron", 15,
            /* production */     "wood", 1,
            /* productionReq */  "iron", 1,
            /* upgrade */        "Big Sawmill",
            /* buildingTime */   5,
        
    ,
    "Smith",
        
            ...
        
    ,
    ...
;

但是,从性能的角度来看,这并不是最优的。大量内存浪费在映射结构和字符串本身上。例如,每次您必须查找建筑物的要求时,都必须遍历几张地图,从而导致代码相当慢。通过为建筑物和资源提供数字标识符以及使用数组而不是地图,可以使其更加优化。要生成标识符,您可以使用enum,例如:

enum Building 
    SAWMILL,
    SMITH,
    BIG_SAWMILL,
    BIG_SMITH, 
    ...
    BUILDING_COUNT
;

enum Resource 
    WOOD,
    IRON,
    ...
    RESOURCE_COUNT
;

class BuildingInfo 
public:
     std::string name;
     int buildingReq[RESOURCE_COUNT];
     int production[RESOURCE_COUNT];
     int productionReq[RESOURCE_COUNT];
     BuildingType upgrade;
     int buildingTime;
;

const BuildingInfo buildings[BUILDING_COUNT] = 
    
        /* name */         "Sawmill",
        /* buildingReq */   15, 15,
        /* production */     1,  0,
        /* productionReq */  0,  1,
        /* upgrade */       BIG_SAWMILL,
        /* buildingTime */  5,
    ,
    
        /* name */         "Smith",
        ...
    ,
    ...
;

但是,后一个示例有点不安全:与enum BuildingType 相比,数组buildings[] 的顺序很容易出错,并且您可能会意外使用来自enum ResourceType 的名称,而您应该使用了一个来自enum BuildingType。一个折衷方案是使用enum class 而不是enum 来识别建筑和资源类型,并继续使用std::map 而不是数组。然后它看起来像:

enum class Building 
    SAWMILL,
    ...,
    BIG_SMITH,
;

enum class Resource 
     WOOD,
     IRON,
;

class BuildingInfo 
public:
     std::map<Resource, int> buildingReq;
     std::map<Resource, int> production;
     std::map<Resource, int> productionReq;
     Building upgrade;
     int buildingTime;
;

const std::map<Building, BuildingInfo> buildings = 
    Building::SAWMILL,
        
            /* buildingReq */   Resource::WOOD, 15, Resource::IRON, 15,
            /* production */    Resource::WOOD, 1,
            /* productionReq */ Resource::IRON, 1,
            /* upgrade */       Building::BIG_SAWMILL,
            /* buildingTime */  5,
        
    ,
    Building::SMITH,
        ...
    ,
    ...
;

请注意,在上述所有示例中,成员变量buildingReqproductionproductionReq 的类型相同。明确说明这一点很有意义:

typedef std::map<Resource, int> ResourceCollection;

class BuildingInfo 
public:
     ResourceCollection buildingReq;
     ResourceCollection production;
     ResourceCollection productionReq;
     ...
;

您可以更进一步,将ResourceCollection 转换为正确的class,具有相互添加和减去资源集合的功能,等等。创建您自己的类型使编译器可以轻松地对您的代码进行类型检查,并在编译时捕获编程错误。

【讨论】:

以上是关于在 C++ 中存储数据,如 python 中的字典的主要内容,如果未能解决你的问题,请参考以下文章

如何用内存高效的数据结构替换大型 python 字典?

Python中的字典

python学习之第六篇:Python中的字典及其所具有的方法

python数据结构-字典

python数据结构-序列之字典

C++ 哈希表查询_进入哈希函数结界的世界