数据复制与封装

时间:2013-11-02 08:20:27

标签: c++ oop data-structures encapsulation

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

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

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

  1. 男子的年龄(出生后数小时)。
  2. 他所居住的城镇的名称。
  3. 管理此数据的方便而自然的方法是创建一个对象,该对象与人对应并将其存储在数组中:

    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;
    }
    

    对象仍然是自包含的,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)来解决数据重复问题,保持对象尽可能自包含

2 个答案:

答案 0 :(得分: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个不同的数组将为您提供存储数据和避免填充的最佳选择(当然您可以使用包...但不建议使用)

答案 1 :(得分:0)

这种问题有一种特殊的设计模式:Flyweight pattern。更好地使用它。