0

我面临的问题是如何结合封装和优化内存使用。

我无法向您展示我的代码,因此无法在广泛的(我希望的)示例中对其进行解释。

假设我们需要一个男性数据库。我们只想知道关于这些人的两件事:

  1. 男子的年龄(以出生后的小时为单位)。
  2. 他居住的城镇名称。

管理这些数据的方便而自然的方法是创建一个对象,该对象对应于一个人并将它们存储在一个数组中:

class OMan1 {
  public:
    OMan( const int &age, const astring &t ): fAge(age), fTown(t) {}
    const int& age() const: { return fAge; }
    const astring& Town() const: { return fTown; }
    astring FullId() const: { return fTown+fAge; }
  private:
    int fAge;
    astring fTown;
}

OMan mans[N];

在这里,我们的 OMans 是自包含的对象,一切都很好。

除了我们将城镇名称克隆数千次,并以这种方式浪费内存和执行时间。

我们可以做的一个改进是为城镇名称和每个 OMan 只存储城镇的年龄和一个指向城镇数组的指针创建独立的数组:

class OMan2 {
  // same functionality as for OMan1
    int fAge;
    int fTownId;
    astring* fTowns;
}

object 仍然是自包含的,sizeof(int) + sizeof(void*) 远小于 sizeof(astring),我们赢了很多。但它仍然是 sizeof(fAge) 的 2-3 倍,我们重复 fTowns 数十亿次。

内存优化对我来说至关重要,因此我只保留 fAge 和 fTownId 并将 Town() 和 FullId() 等功能从 OMan 类移到某个 OManDataBase 类:

class OMan3 {
  public:
    OMan( const int &age, const int &tid ): fAge(age), fTownId(tid) {}
    const int& age() const: { return fAge; }
    const int& TownId() const: { return fId; }
    // const astring& Town() const: { return fTown; }
    // astring FullId() const: { return fTown+fAge; }
  private:
    int fAge;
    int fTownId;
}

class OManDataBase {
  // constructor, destructor
    const int& age( const int& i) const: { return fMans[i].TownId()]; }
    const astring& Town( const int& i) const: { return fTown[fMans[i].TownId()]; }
    const astring& FullId( const int& i) const: { return Town(i)+age(i); }
  private:
    vector<OMan3> fMans;
    vector<astring> fTowns;
}

OMan3 现在不是自包含对象。例如,它不知道自己的全名。这意味着如果我需要和一个人一起做一些数据处理,我必须使用整个 OManDataBase 实例:

OBillType47 NewBillType47( const OManDataBase &db, int i ) { ... }

代替

OBillType47 NewBillType47( const OMan &m ) { ... }

这里的封装被打破,代码可读性明显降低。(我放 Type47 是为了强调我可以有很多功能,这些功能与 Oman-S 一起使用,并且不能将它们全部包含在 OManDataBase 类中)。

我想知道是否有任何其他方法(-s)来解决数据重复问题,使对象尽可能自包含

4

2 回答 2

0

针对这样的问题有一种特殊的设计模式:享元模式。更好地使用它。

于 2013-11-19T15:06:29.943 回答
0

您可以像这样创建一个通用类:

enum class Towns {T1 = 0, T2 = 1...}
string TownsNames[] = {"T1", "T2"...}

class OManDB 
{
   map<Towns, OMan*> m;
// OR
   map<int, Towns> m; // map by Oman ID and town

public:
   void addOMan();
   Oman getOManById(int id);
   OMan *getOManArrByTown(Towns town);
   OMan getOManTown(int omanId);
   ...
}

这个想法是创建一个类来保存所有记录并为您执行所有操作,例如数据库,您没有数据集并且一组操作彼此分开。一切都在一个地方,您可以根据需要定义操作。

这样,您可以随意更改内部表示,您可以返回一个 OMan 数组,您基本上可以在不破坏退出代码的情况下做任何您想做的事情。用户会知道他/她可以获得 OMan 并且不需要知道存储它的字段的确切方法。您可以将它们存储在单个 64 位字段中(例如,如果 ID 只有 32 位,而城镇是一个额外的 32 位字段)。它给你自由。

更新

好了,我们暂时先抛开封装,来处理性能。理想情况下,对于每一位内存,您可以将 ID 和城镇 ID 存储在 2 个不同的向量中,如果 2 个字段的大小不同并且编译器必须进行填充,这将产生重要的优势。使用 2 个不同的数组将为您提供存储数据并避免填充的最佳选择(当然您可以使用 pack...,但不推荐)

于 2013-11-02T08:41:49.910 回答