J.T*_*J.T 4 c++ arrays data-structures
我有一些我需要组织的数据.我应该注意到我是C和C++的新手.
我有一个建筑物清单,每个建筑物都有一定数量和与之相关的资源费率数组.我需要遍历建筑物列表,将每个建筑物的数量乘以其资源差异,并将它们相加以得出总资源差异.
例如:
冰井:[-100功率,+ 50水],n = 2
太阳能阵列:[+150功率],n = 2
总计:[+100电力,+ 100水]
我对如何组织我的数据感到困惑.如果我使用结构,循环将是一个挑战,但一旦建筑类型的数量增加,阵列将会混淆.在这一点上,我最好的猜测是将枚举与数组耦合,以获得具有数组循环功能的结构的命名功能.我应该怎么做呢?
您可能想要考虑这样的方法:(实例: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;
}
Run Code Online (Sandbox Code Playgroud)
基类,Building定义接口; 基本上它列出了您的建筑物可能产生或消耗的资源.派生类IceWell和SolarArray然后实现该接口,定义使一个这样的建筑将源或汇的资源的量.最后,你有一个包含一些派生对象实例的向量(保存在共享指针中,为你处理内存管理!).您可以遍历这些并累积每个资源的总值.
如果生产/消费值可能随时间而变化,则可以将这些值保存为每个派生类的(私有)成员变量,并允许某些机制对其进行修改.您甚至可以getWater()根据某个时间度计算其值,因此随着时间的推移,井可能会"干涸".
在发布我的原始答案(上图)之后,我发现自己在考虑替代方案.我写了以下内容,试图解释一些设计选项,它们的优点和缺点,以及如何对这样的系统设计进行推理.我希望它有所帮助.
在C++中很少能找到"单一正确的解决方案".通常有几种合理的方法,每种方法都有其自身的一套相对优点和缺点.
当您使用C++设计程序时,您将不可避免地要考虑一些可能的解决方案,并根据其优点选择一个.让我们试着分析这里陈述的问题......
您有一些建筑物,这些建筑物具有定义其资源使用情况的属性.一些建筑物是净生产者,而其他建筑物是特定资源的净消费者.这会立即将"资源"标识为系统中的实体.我们可能直接进入并编写一些封装了我们的净资源使用概念的东西:
struct ResourceFootprint {
int power;
int water;
};
Run Code Online (Sandbox Code Playgroud)
您现在可以通过适当地实例化该结构来表示不同级别的资源消耗:
(实例:http://ideone.com/ubJv8m)
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;
}
Run Code Online (Sandbox Code Playgroud)
这看起来不错; 我们已经打包了资源,它们有方便的名称,我们可以通过提供相关的价值来创建新的建筑类型.这意味着我们可以拥有存储在文件中的建筑物库存,其中可能包含以下内容:
IceWell,-100,50
SolarArray,150,0
HydroElectricPlant,150,150
Run Code Online (Sandbox Code Playgroud)
我们所要做的就是读取文件并创建ResourceFootprint的实例.这似乎是一个合理的解决方案; 毕竟,不同种类的资源可能是合理固定的,而生产和消费它们的不同种类的建筑可能经常发生变化.您可能想要添加医院,加油站,餐厅和农场.
可是等等; 医院也消耗医药,并生产医疗废物.加油站肯定需要石油,餐馆将消耗食物,水和电力,农场可能生产食物,但他们将需要石油,电力和水这样做.
现在我们处于不同种类的资源必须改变的情况.目前的机制仍然可行; 我们可以将资源添加到我们的结构中:
struct ResourceFootprint {
int power;
int water;
int oil;
int food;
int medicine;
int medical_waste;
};
Run Code Online (Sandbox Code Playgroud)
之前没有使用新资源的事情不必关心(尽管定义可能需要将额外的资源字段归零),并且使用新资源的事物可以用它们来定义:
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};
Run Code Online (Sandbox Code Playgroud)
更好的是,计算总功率和用水量的现有代码仍然可以正常工作.我们可以只为其他资源添加代码:
(实例:http://ideone.com/rukqaz)
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;
}
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.但这种方法的缺点是什么?
嗯,一个明显的问题是资源占用空间只是普通数据.当然,我们可以改变这些值,但我们不能真正做任何复杂的事情,比如随着时间的推移让IceWell干涸,或者允许SolarArray根据它是白天还是晚上产生不同的功率.为此,我们需要以某种方式计算资源足迹,这种方式可能因建筑类型而异.
答案的原始部分探讨了一种解决方案,其中每个建筑物都有自己的类型,其中成员函数返回相关资源的当前消耗.正如我们刚刚探讨的那样,我们可能需要扩展我们的资源集.我们可以通过结合这两个想法来做到这一点; 每个建筑都有一个类,以及一个用于保存资源使用的结构.然后,每个构建类可以决定如何实现其资源使用.
基类看起来像这样:
class Building {
public:
virtual ~Building() {}
virtual ResourceFootprint currentResourceLevel() = 0;
};
Run Code Online (Sandbox Code Playgroud)
我们选择按值返回ResourceFootprint(而不是返回引用或任何其他方法),因为这允许我们轻松地更改实现.讨论如下......
在最简单的情况下,水力发电厂可能只是使用恒定的水供应,并产生恒定的电力供应.在这里,它会将ResourceFootprint对象保存为(可能是const)成员变量,并在询问其资源消耗时返回它的副本:
class HydroElectricPlant : public Building {
public:
HydroElectricPlant(const ResourceFootprint& r)
: resources(r) {}
virtual ResourceFootprint currentResourceLevel() { return resources; }
private:
const ResourceFootprint resources;
};
Run Code Online (Sandbox Code Playgroud)
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;
};
Run Code Online (Sandbox Code Playgroud)
而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;
};
Run Code Online (Sandbox Code Playgroud)
该设计现在允许:
这不包括什么?
当然,还有其他解决方案,但我希望这能为您在使用C++设计系统时需要采用的思维过程提供一些洞察力.很可能你会想出一个你满意的设计,通过你的程序中途,然后意识到你已经碰壁,你的设计根本无法工作.没关系,它发生了.回去再次进行设计阶段,掌握第一个出错的地方.