gpt4 book ai didi

c++ - 用于多层数据组织的结构与数组

转载 作者:行者123 更新时间:2023-12-02 07:40:03 25 4
gpt4 key购买 nike

我有一些需要整理的数据。我应该注意,我是C和C++的新手。

我有一个建筑物 list ,每座建筑物都有与之相关的数量和资源率数组。我需要遍历建筑物列表,将每座建筑物的数量乘以其资源差异,然后将它们相加得出总资源差异。

例如:

冰井:[-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;
}

基类 Building定义了接口(interface);本质上,它列出了建筑物可能生产或消耗的资源。然后,派生类 IceWellSolarArray实现该接口(interface),定义一个此类建筑物将源或吸收的资源量。最后,您有一个包含一定数量的派生对象实例的 vector (保存在共享指针中,可以为您处理内存管理!)。您可以遍历这些值并累积每个资源的总值。

如果生产/消费值可能随时间变化,则可以将其作为每个派生类的(私有(private))成员变量保存,并允许某种机制对其进行修改。您甚至可以让 getWater()根据某个时间量度来计算其值,因此随着时间的流逝,油井可能会“枯竭”。

发布我的原始答案(上面)后,我发现自己正在考虑替代方法。我写了以下文章,试图解释一些设计方案,它们的优缺点以及如何进行这样的系统设计推理。希望对您有所帮助。

在C++中很少找到“单一正确的解决方案”。通常有几种合理的方法,每种方法都有其相对的优缺点。

当您使用C++设计程序时,不可避免地必须考虑一些可能的解决方案,并根据其优点选择一个。让我们尝试分析此处所述的问题...

您有一些建筑物,这些建筑物具有定义其资源使用情况的属性。一些建筑物是净生产者,而其他建筑物是特定资源的净消费者。这立即将“资源”标识为系统内的实体。我们可能会直接进入并编写一些代码,以封装我们对网络资源使用的想法:
struct ResourceFootprint {
int power;
int water;
};

现在,您可以通过适当地实例化该结构来表示不同级别的资源消耗:

(实时示例: 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;
}

这看起来不错;我们已经打包了资源,它们有方便的名称,并且仅通过提供相关值就可以创建新的建筑物。这意味着我们可以将建筑 list 存储在文件中,该 list 可能类似于以下内容:
IceWell,-100,50
SolarArray,150,0
HydroElectricPlant,150,150

我们要做的就是读取文件并创建ResourceFootprint的实例。这似乎是一个合理的解决方案。毕竟,不同种类的资源可能是合理固定的,而生产和使用它们的不同种类的建筑物却可能经常变化。您可能要添加医院,加油站,餐厅和农场。

可是等等;医院还消耗药品,并产生MedicalWaste。加油站肯定需要石油,饭店将消耗食物以及水和电力,而农场可能会生产食物,但他们将需要石油,电力和水。

现在,我们处于必须更改各种资源的情况。目前的机制仍然可行;我们可以将资源添加到我们的结构中:
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};

更好的是,用于计算总电力和用水量的现有代码仍然可以正常工作。我们可以将其他资源的代码添加到其中:

(实时示例: 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;
}

到目前为止,一切都很好。但是这种方法的缺点是什么?

好吧,一个明显的问题是资源占用量只是纯数据。当然,我们可以更改这些值,但是我们实际上不能做任何复杂的事情,例如随着时间的流逝使IceWell干dry,或者允许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;

};

该设计现在允许:
  • 添加新种类的资源:当将新字段添加到结构中时,以上代码将继续工作(尽管您可能需要对结构的初始化进行一些更改以解决这些新字段)。
  • 添加新种类的建筑物:您可以编写另一个继承自Building的类,并根据需要定义其资源使用情况。

  • 这个没有盖什么?
  • 动态创建建筑类型。第一个解决方案是仅根据ResourceFootprint字段的值定义建筑物,这使我们能够通过仅使用具有适当值的ResourceFootprint的新实例来创建新型建筑物。这非常适合允许您制作自定义建筑物的系统,但是在这些值如何随时间变化时并没有提供太大的灵活性。
  • 无需添加更多代码即可处理新的资源类型。每个新资源类型在结构中都有一个字段,因此您需要按名称引用它。您可以仅持有一个std::vector来按特定顺序解释其元素(例如res [0]为水,res [1]为幂)以实现此目标,但为了实现该目的而牺牲了代码的清晰度。
  • 我还没有想到的无数其他可能性!

  • 当然,还有其他解决方案,但是我希望这可以使您对使用C++设计系统时需要采用的思维过程有所了解。您很可能会提出自己满意的设计,进入程序的一半,然后意识到您碰到了根本无法使用设计的问题。很好,它确实发生了。回到第一个设计阶段,并了解第一个出错的地方。

    关于c++ - 用于多层数据组织的结构与数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25083659/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com