我有一些我需要组织的数据。我应该注意到我是C和C ++的新手。
我有一个建筑物清单,每个建筑物都有一定数量和与之相关的资源费率数组。我需要遍历建筑物列表,将每个建筑物的数量乘以其资源差异,并将它们相加以得出总资源差异。
例如:
冰井:[ - 100次方,+ 50水],n = 2
太阳能电池阵列:[+150电力],n = 2
总计: [+ 100电力,+ 100水]
我对如何整理数据感到困惑。如果我使用结构,循环将是一个挑战,但一旦建筑类型的数量增加,阵列将会混淆。在这一点上,我最好的猜测是将枚举与数组耦合,以获得具有数组循环功能的结构的命名功能。我应该怎么做呢?
答案 0 :(得分:8)
您可能需要考虑这样的方法:(实例:http://ideone.com/xvYFXp)
#include <iostream>
#include <vector>
#include <memory>
class Building {
public:
virtual int getPower() = 0;
virtual int getWater() = 0;
virtual ~Building() {}
};
class IceWell : public Building {
public:
virtual int getPower() {return -100;}
virtual int getWater() {return 50;}
};
class SolarArray : public Building {
public:
virtual int getPower() { return 150; }
virtual int getWater() { return 0; }
};
int main()
{
std::vector<std::shared_ptr<Building>> inventory;
inventory.emplace_back(new IceWell);
inventory.emplace_back(new SolarArray);
inventory.emplace_back(new IceWell);
inventory.emplace_back(new SolarArray);
int total_power = 0;
int total_water = 0;
for (auto building : inventory)
{
total_power += building->getPower();
total_water += building->getWater();
}
std::cout << "Total power: " << total_power << "\n";
std::cout << "Total water: " << total_water << "\n";
return 0;
}
基类Building
定义接口;基本上它列出了您的建筑物可能产生或消耗的资源。派生类IceWell
和SolarArray
然后实现该接口,定义一个这样的建筑物将来源或接收的资源量。最后,你有一个包含一些派生对象实例的向量(保存在共享指针中,为你处理内存管理!)。您可以遍历这些并累积每个资源的总值。
如果生产/消费值可能随时间而变化,您可以将这些值保存为每个派生类的(私有)成员变量,并允许某些机制修改它们。您甚至可以getWater()
计算其值作为一定时间的函数,因此井可能会干涸&#34;随着时间的推移。
在发布我的原始答案(上图)之后,我发现自己正在考虑替代方案。我写了以下内容,试图解释一些设计选项,它们的优点和缺点,以及如何对这样的系统设计进行推理。我希望它有所帮助。
在C ++中很少能找到一个正确的解决方案&#34;。通常有几种合理的方法,每种方法都有自己的一套相对优点和缺点。
当您使用C ++设计程序时,您将不可避免地要考虑一些可能的解决方案,并根据其优点选择一个。让我们尝试分析这里所说的问题......
您有一些建筑物,这些建筑物具有定义其资源使用情况的属性。一些建筑物是净生产者,而其他建筑物是特定资源的净消费者。这立即识别出资源&#34;作为系统内的实体。我们可能直接进入并编写一些封装了我们净资源使用概念的东西:
struct ResourceFootprint {
int power;
int water;
};
现在,您可以通过适当地实例化该结构来表示不同级别的资源消耗:
int main()
{
ResourceFootprint ice_well = {-100, +50};
ResourceFootprint solar_array = {+150, 0};
std::vector<ResourceFootprint> buildings;
buildings.push_back(ice_well);
buildings.push_back(ice_well);
buildings.push_back(solar_array);
buildings.push_back(solar_array);
ResourceFootprint total = {0, 0};
for (const ResourceFootprint& r : buildings)
{
total.power += r.power;
total.water += r.water;
}
std::cout << "P: " << total.power << ", W: " << total.water << "\n";
return 0;
}
这看起来不错;我们已经打包了资源,他们有方便的名字,我们可以通过提供相关的价值来创建新的建筑类型。这意味着我们可以拥有存储在文件中的建筑物清单,其中可能包含以下内容:
IceWell,-100,50
SolarArray,150,0
HydroElectricPlant,150,150
我们所要做的就是读取文件并创建ResourceFootprint的实例。这似乎是一个合理的解决方案;毕竟,不同种类的资源可能是合理固定的,而生产和消费它们的不同种类的建筑可能经常发生变化。您可能想要添加医院,加油站,餐厅和农场。
但等等;医院也消耗医药,并生产医疗废物。加油站肯定需要石油,餐馆将消耗食物,水和电力,农场可能生产食物,但他们将需要石油,电力和水这样做。
现在我们处于不同类型的资源必须改变的情况。目前的机制仍然可行;我们可以将资源添加到我们的结构中:
struct ResourceFootprint {
int power;
int water;
int oil;
int food;
int medicine;
int medical_waste;
};
在不必关心之前没有使用新资源的事情(虽然定义可能需要将额外的资源字段清零),并且可以在以下内容中定义使用新资源的事物。他们的条款:
ResourceFootprint ice_well = {-100, +50, 0, 0, 0, 0};
ResourceFootprint solar_array = {+150, 0, 0, 0, 0, 0};
ResourceFootprint hospital = {-100, -50, 0, -50, -50, 50};
ResourceFootprint gas_station = {-10, 0, -100, 0, 0, 0};
ResourceFootprint restaurant = {-20, -20, 0, -100, 0, 0};
ResourceFootprint farm = {-10, -30, -10, 200, 0, 0};
更好的是,计算总功率和用水量的现有代码仍然可以正常工作。我们可以只为其他资源添加代码:
ResourceFootprint total = {0, 0, 0, 0, 0, 0};
for (const ResourceFootprint& r : buildings)
{
total.power += r.power;
total.water += r.water;
total.oil += r.oil;
total.food += r.food;
total.medicine += r.medicine;
total.medical_waste += r.medical_waste;
}
到目前为止,这么好。但这种方法的缺点是什么?
嗯,一个明显的问题是资源占用空间只是普通数据。当然,我们可以改变这些值,但我们无法做任何复杂的事情,比如随着时间的推移让IceWell干涸,或者允许SolarArray根据是白天还是晚上产生不同的功率。为此,我们需要以某种方式计算资源足迹,这种方式可能因建筑类型而异。
答案的原始部分探讨了一种解决方案,其中每个建筑物都有自己的类型,其成员函数返回相关资源的当前消耗。正如我们刚刚探讨的那样,我们可能需要扩展我们的资源集。我们可以通过结合这两个想法来做到这一点;每个建筑都有一个类,以及一个用于保存资源使用的结构。然后,每个构建类可以决定如何实现其资源使用。
基类看起来像这样:
class Building {
public:
virtual ~Building() {}
virtual ResourceFootprint currentResourceLevel() = 0;
};
我们选择按值返回ResourceFootprint(而不是返回引用或任何其他方法),因为这允许我们轻松地更改实现。讨论如下......
在最简单的情况下,水力发电厂可能只使用恒定的水供应,并产生恒定的电力供应。在这里,它会将ResourceFootprint对象保存为(可能是const)成员变量,并在询问其资源消耗时返回它的副本:
class HydroElectricPlant : public Building {
public:
HydroElectricPlant(const ResourceFootprint& r)
: resources(r) {}
virtual ResourceFootprint currentResourceLevel() { return resources; }
private:
const ResourceFootprint resources;
};
IceWell可能会做一些更复杂的事情:
class IceWell : public Building {
public:
IceWell(const ResourceFootprint& initial)
: resources(initial) {}
virtual ResourceFootprint currentResourceLevel() { return resources; }
void useWater(int amount) { resources.water -= amount; }
private:
ResourceFootprint resources;
};
SolarArray可能会:
class SolarArray : public Building {
public:
SolarArray(const ResourceFootprint& r)
: day_resources(r), night_resources(r)
{
night_resources.power = 0;
}
virtual ResourceFootprint currentResourceLevel()
{
if (is_day())
{
return day_resources;
}
else
{
return night_resources;
}
}
private:
ResourceFootprint day_resources;
ResourceFootprint night_resources;
};
设计现在允许:
这不包括什么?
当然,还有其他解决方案,但我希望这能为您在使用C ++设计系统时需要采用的思维过程提供一些洞察力。很可能你会想出一个你满意的设计,通过你的程序中途,然后意识到你已经碰壁,你的设计根本无法工作。这很好,它发生了。回去再次进行设计阶段,掌握第一个出错的地方。
答案 1 :(得分:2)
有一个关于你的问题的功能值得多关注:你还不知道最后会使用什么资源。您甚至不知道是否要在未来的版本2.0中添加一两个资源,也许允许您的用户定义自己的资源等等。所以我认为,您现在应该考虑更通用的方法。
那么您的建筑需要什么样的行为?他们需要能够告诉他们如何修改未知名称的资源。他们可能或者可能不必知道他们修改了哪些资源,但资源更新代码应该能够获取一些资源列表及其关联状态,并从中计算结果状态。
因此,我可能会编写类似这样的代码:
class Building { //might be abstract
public:
virtual int getResourceDiff(std::string resource); //might be pure virtual
};
和其他地方
//the header
class SomeGameWorldClass {
private:
std::unordered_map<std::string, int> resources;
std::vector<std::shared_ptr<Building> > buildings;
public:
void updateResources();
};
//the .cpp file
void SomeGameWorldClass::updateResources() {
for(resourcePair : resources) {
for(buildingPtr : buildings) {
resourcePair.second += buildingPtr->getResourceDiff(resourcePair.first);
}
}
}
这使您可以随意定义新资源,并且可以自由地以您想要的任何方式计算资源更新,具体取决于建筑物的类型。哎呀,你甚至可以定义一个Building
- 子类来运行一些用户提供的场景控制代码来进行计算。
当然,我现在只使用哈希映射的字符串/整数对来存储名称和资源的当前数量。最有可能的是,这还不够,您可能需要在资源中添加更多行为,例如设置上限存储限制等等。因此,您很可能最终会使用另一个类来描述您的资源:
class Resource {
public:
...
private:
int curAmount, maxAmount;
std::string name;
...
};
答案 2 :(得分:1)
尽量使用字符串来表示建筑类型:
struct Building
{
Building(std::string n, int p, int w) : name(n), power(p), water(w) {}
std::string name;
int power;
int water;
};
int main()
{
Building iceWell("Ice Well", -100, 50);
Building solar("Solar Array", 150, 0);
Building buildings[4] = { iceWell, iceWell, solar, solar };
int totalPower = 0;
int totalWater = 0;
for (int i = 0; i < 4; ++i)
{
totalPower += buildings[i].power;
totalWater += buildings[i].water;
}
}
这可以改进,例如使用std::vector
或任何适合您的问题的集合而不是数组。