用于保存多种数据类型C ++的数据结构

时间:2014-10-02 08:34:50

标签: c++

在我的应用程序中有一个类(即ItemData类),它有30多个不同类型的成员变量,如

int a;
int b;
std::string c;
float d;
double e;
double f;
char * g;

还有更多。

我的应用程序需要创建大量的itemdata类,以便应用程序的内存使用率很高。关于这个类的一个特殊事实是,ItemData类的大多数实例仅具有少数成员变量的值。

例如:实例1可能只有a和b的值。实例2只能包含b,c和g的值。

因此,为了减少应用程序的内存使用量,我需要为仅在创建实例时具有数据的成员变量分配内存。 所以我虽然拥有通用数据结构,可以通过位置访问元素并在其上存储数据。因此ItemData类具有类似下面的内容并存储动态分配的数据。(我必须单独保持每个位置的位置和信息)

std::vector<void*> vec_DataArray;

如果ItemData实例mItemData1具有a和d的值:

mItemData1.vec_DataArray[0] = new int(iValue);
mItemData1.vec_DataArray[3] = new float(fValue);

有人可以告诉我这是否是减少应用程序内存使用量的好方法?是否有任何通用容器可以容纳多种数据类型(对于vec_DataArray),以便在访问数据时可以避免void *到数据类型转换。

5 个答案:

答案 0 :(得分:2)

我认为如果可以考虑不同的设计,你应该避免使用可以容纳多种类型数据的数据结构。

例如,您可以考虑使用类似于Flyweight pattern的内容并拉出&#34;外在&#34;退出ItemData课程,只留下&#34;内在的&#34;州。它对我来说有点不面向对象,但它可能满足您的需求。例如,您可以将项目索引与数据分开保存。这是否有助于内存使用取决于数据的稀疏程度。

#include <unordered_map>
#include <iostream>

class Item {
 private:
  std::string type_;  // "intrinsic" state
 public:
  Item(const std::string &type) : type_(type) {} 

  // pass in "extrinsic" state
  void someOperation(std::string color, double speed) {

    // do something using both "intrinsic" and "extrinsic" state...
    std::cout << color << " " << type_ << " moving at " << speed << "mph\n";
  }
};

class AnimalSim {
 private:
  std::unordered_map<int, std::string> duck_colors_;   // Store extrinsic
  std::unordered_map<int, std::string> sheep_colors_;  // state separately
  std::unordered_map<int, double>      duck_speeds_;   // in a more
  std::unordered_map<int, double>      sheep_speeds_;  // efficent way.
 public:
  void run();
  std::string getDuckColor(int duck_index) const;
  std::string getSheepColor(int sheep_index) const;
  double      getDuckSpeed(int duck_index) const;
  double      getSheepSpeed(int sheep_index) const;
};

void
AnimalSim::run() {
  auto duck = Item{"duck"};    // Create `Flyweight` objects that can be shared.
  auto sheep = Item{"sheep"};  // Should probably be done by a factory with a cache.

  // Create duck 0
  duck_colors_.emplace(0, "red");
  duck_speeds_.emplace(0, 150.0);

  // Create duck 1 - has no speed
  duck_colors_.emplace(1, "green");
  size_t num_ducks = 2;

  // Create sheep 0 - has no color
  sheep_speeds_.emplace(0, 100.0);
  size_t num_sheep = 1;

  // Do something with all the ducks
  for(size_t i = 0; i != num_ducks; ++i)
    duck.someOperation(getDuckColor(i), getDuckSpeed(i));

  // Do something with all the sheep
  for(size_t i = 0; i != num_sheep; ++i)
    sheep.someOperation(getSheepColor(i), getSheepSpeed(i));    
}

std::string
AnimalSim::getDuckColor(int duck_index) const {
  auto color_itr = duck_colors_.find(duck_index);
  return color_itr != duck_colors_.end() ? color_itr->second : "black"; 
}

std::string
AnimalSim::getSheepColor(int sheep_index) const {
  auto color_itr = sheep_colors_.find(sheep_index);
  return color_itr != sheep_colors_.end() ? color_itr->second : "white"; 
}

double
AnimalSim::getDuckSpeed(int duck_index) const {
  auto speed_itr = duck_speeds_.find(duck_index);
  return speed_itr != duck_speeds_.end() ? speed_itr->second : 0.0; 
}

double
AnimalSim::getSheepSpeed(int sheep_index) const {
  auto speed_itr = sheep_speeds_.find(sheep_index);
  return speed_itr != sheep_speeds_.end() ? speed_itr->second : 0.0; 
}

int main() {
  AnimalSim animal_sim;
  animal_sim.run();
}

Live demo.

编辑:我看到您正在从数据库加载数据。在这种情况下,我想知道为什么你不只是在需要时从数据库加载按需?

答案 1 :(得分:1)

在堆上分配原始类型是一种非常非常糟糕的方法。您将获得巨大的内存管理开销,添加间接,并且您的内存使用将会通过顶层,因为每个基本类型的分配将在内存池中至少占用3个地址。 使用(区分的)联合用于原始类型。如果使用构造类型创建联合,则必须手动管理对构造函数和析构函数的调用。

答案 2 :(得分:1)

您的问题的一个可能解决方案是推迟关于记录的实际组成的决定,直到您需要处理它为止。例如,如果要解析文件中的文本字符串,请不要在读取时解析它;传递或存储实际的文本行。当您需要进程字段时,按需解析它。这被称为“延迟解析”,它避免了存储不确定类型的问题......不确定类型。

答案 3 :(得分:1)

您有一个公司ID和一组各种类型的公司描述符,其中每种描述符都有一个唯一标记(“列”),以及ID和集合之间的某种映射。所以你有map<company_id_t, set<company_detail_t>>之类的东西。请注意,该集不必是std::set,您可以轻松构建自己的集合vector。基于company_detail_t的解决方案,但从概念上讲它是一个集合。

detail_tag_t可以是一对detail_value_tboost::variant。该集仅限于标记,因此没有两个细节具有相同的标记。值可能是

  1. 一个工会
  2. a boost_any
  3. a detail_value_base
  4. 指向具有不同子类的detail_tag_t类对象的指针。在这种情况下,detail_tag_t detail_value_base::getTag()实际上可能是虚拟{{1}}函数的结果。

答案 4 :(得分:1)

C ++具有多种类型的多数据存储结构,如上面提到的boost :: any或boost :: variant或build it变体以及任何来自std的

但是,如果您想自己实现一个。我在下面构建的该类可以演示可以构建以存储多种数据类型的东西。如果删除联合类。您可以仅从一个变量存储和获取多种类型的数据。但是,如果删除联合类。内存使用效率不会那么高。联合类型可以解决许多问题。但是,如果要将std :: string存储到联合中,则不能这样做。

您可以根据需要扩展类型。

如果使用此功能,则可以执行以下操作: _cDynamic a; a =(整数)124。

然后过一会儿,您不知道变量中存储了什么,然后可以执行a.check()。 check()将返回存储在实例中的最新数据类型。

如果可以确定其中存储了什么,则可以使用getS(),getI(),getUI()等来获取数据类型。没有错误检查。或者您可以使用:something = get(&variable)。或者,您可以使用getVoid(),并且将返回指向最新数据类型的void指针。如果您使用getVoid(),则需要知道数据类型并正确进行转换。

因此,如果您将int存储为最新值,则执行a.check()将返回std :: string的“ int”。为了提高效率,每次将新数据类型设置为a时,a都不会删除最后存储的内容。无论如何,对于联合中定义的任何内容都不需要。但是,对于字符串类,情况会有所不同。但是,您需要的是内存效率。因此,您可以修改代码,以便将非std :: string的任何内容设置为实例,然后将vString变量重置为空。这应该有助于释放一些所需的内存。如果您不使用字符串,只需从类中删除字符串类型。这将有助于减少内存使用量,因为联合类型将仅使用那么多的内存,而该内存将与定义要容纳的最大数据类型一样大。另外,您可以从类中删除所有不需要的不必要的功能。

此外,current可以是一个无符号字符,以使用更少的内存。您可以通过数字键入您的系统。但是,我使用std :: string以便通过查看可以轻松地对其进行引用。

#include <map>
#include <iostream>

union _uV {
    int           i;     
    unsigned int ui;     
    long          l;     
    double        d;     
    float         f;     
    char          c;     
    bool          b;     
};

class _cDynamic{ 
    std::string vString;     
    std::string current = "null";

    static std::string intname;
    static std::string uintname;
    static std::string longname;
    static std::string doublename;
    static std::string floatname;
    static std::string charname;
    static std::string boolname;
    static std::string stringname;

    _uV vNorm;

    template<class T>
    void set(const T &v, const std::string vname){
        const void * p = &v;

        if ( vname == intname ) {
            vNorm.i = *static_cast<const int *> (p);
            current = "int";
        } else if ( vname == uintname ){
            vNorm.ui = *static_cast<const unsigned int *> (p);
            current = "uint";
        } else if ( vname == longname ){
            vNorm.l = *static_cast<const long *> (p);
            current = "long";
        } else if ( vname == doublename ){
            vNorm.d = *static_cast<const double *> (p);
            current = "double";
        } else if ( vname == floatname ){
            vNorm.f = *static_cast<const float *> (p);
            current = "float";
        } else if ( vname == charname ){
            vNorm.c = *static_cast<const char *> (p);
            current = "char";
        } else if ( vname == boolname ){
            vNorm.b = *static_cast<const bool *> (p);
            current = "bool";
        } else if ( vname == stringname ){
            vString = *static_cast<const std::string *> (p);
            current = "string";
        } else {
            throw "unsupported type.";
        }
    };

    public:

    template<class T>
    _cDynamic operator=(const T &v)
    {    
        std::string thisname = typeid(this).name();
        std::string vname = typeid(v).name();
        if ( vname == thisname ) return *this;
        set(v, vname);
        return *this;
    };

    _cDynamic(){};
    template<class T>
    _cDynamic(const T &v)
    {
        std::string vname = typeid(v).name();
        set(v, vname);
    };

    bool get(int &v){
        if ( current == "int" ){
            v = vNorm.i;
            return true;
        } else return false;
    };

    bool get(unsigned int &v){
        if ( current == "uint" ){
            v = vNorm.ui;
            return true;
        } else return false;
    };    

    bool get(long &v){
        if ( current == "long" ){
            v = vNorm.l;
            return true;
        } else return false;
    };

    bool get(double &v){
        if ( current == "double" ){
            v = vNorm.d;
            return true;
        } else return false;
    };

    bool get(float &v){
        if ( current == "float" ){
            v = vNorm.f;
            return true;
        } else return false;
    };

    bool get(char &v){
        if ( current == "char" ){
            v = vNorm.c;
            return true;
        } else return false;
    };


    bool get(bool &v){
        if ( current == "bool" ){
            v = vNorm.b;
            return true;
        } else return false;
    };

    bool get(std::string &v){
        if ( current == "string" ){
            v = vString;
            return true;
        } else return false;
    };

    void * getVoid(){
        void * p;
        if ( current == "int" ){ 
            p = &vNorm.i;
            return p;
        } else if ( current == "uint" ){
            p = &vNorm.ui;
            return p;
        } else if ( current == "long" ){
            p = &vNorm.l;
             return p;
        } else if ( current == "double" ){
            p = &vNorm.d;
              return p;
         } else if ( current == "float" ){
            p = &vNorm.f;
              return p;
        } else if ( current == "char" ){
            p = &vNorm.c;
            return p;
        } else if ( current == "bool" ){
            p = &vNorm.b;
            return p;
        } else if ( current == "string" ){
            p = &vString;
            return p;
        } else throw "Unitialized";
    };

    int getI(){ return vNorm.i; };
    unsigned int getUI() { return vNorm.ui; };
    long getL() { return vNorm.l; };
    double getD() { return vNorm.d; };
    float getF() { return vNorm.f; };
    char getC() { return vNorm.c; };
    bool getB() { return vNorm.b; };
    std::string getS() { return vString; };


    std::string check() { return current; };
};

std::string _cDynamic::intname = typeid(int).name();
std::string _cDynamic::uintname = typeid(unsigned int).name();
std::string _cDynamic::longname = typeid(long).name();
std::string _cDynamic::doublename = typeid(double).name();
std::string _cDynamic::floatname = typeid(float).name();
std::string _cDynamic::charname = typeid(char).name();
std::string _cDynamic::boolname = typeid(bool).name();
std::string _cDynamic::stringname = typeid(std::string).name();

int main(){

    int test; bool ok;
    std::map <int, _cDynamic> a = { {0, std::string("kevin")} };
    std::cout << a[0].check() << " " << a[0].getS() << std::endl;
    a[0] = (int) 1234;
    std::cout << a[0].check() << " " << a[0].getI() << std::endl;
    a[0] = std::string("This is a string");
    std::cout << a[0].check() << " " << a[0].getS() << std::endl;
    a[0] = (float)124.55;
    std::cout << a[0].check() << " " << a[0].getF() << std::endl;
    a[0] = (bool)true;
    std::cout << a[0].check() << " " << a[0].getB() << std::endl;
    a[0] = 'z';
    std::cout << a[0].check() << " " << a[0].getC() << std::endl;


    a[0] = (int)1234;
    ok = a[0].get(test);
    std::cout << a[0].check() << ": is ok("<< ok << ") " << test << std::endl;

    a[0] = (int)2345;
    test = *static_cast<int*>(a[0].getVoid());
    std::cout << a[0].check() << " " << test << std::endl;

    return 0;
}

下面的另一个例子。您可以将许多类型存储到一个变量中。下面的这一类几乎与上面的类似。但是,每次给它分配内容时,它将存储它并标记它具有该类型的数据。这可能不适合您的情况,但是,如果您正确修改下面的代码,则可以以节省内存而不是利用更多内存的方式组合数据。您还可以修改以下代码,以便在需要时可以存储多个字符串,也可以存储任何其他数据类型中的一种以上。

对于以下代码,您可以使用variable.stored()获取存储内容的映射。该映射将包含8个索引,这些索引对应于将什么数据存储到实例中。您可以使用variable.latest()查看最后存储的数据。但是,与最新消息不同,最新消息对此没有太大影响。

类似于上述内容,可以使用get(&v)或getI(),getUI()等来获取数据。没有理由为此使用getVoid()。检查不是get()的内容也没有错误;

注意:如果您已经将数据类型存储到实例中,然后又将相同类型存储到实例中,则新数据将替换旧数据。

下面的一个例子是:

_cDynamicx a;
a = (int)123;
a = std::string("me");

if ( a.stored()["string"] == true ){
    std::cout << a.getS() << std::endl;
} else {
    std::cout << "A did not have string.";
}

这两个功能都是我自己构建的,并且在我作为MIT许可的我的库中,因此如果愿意,可以随意进行修改和使用。

class _cDynamicx{ 
    int vInt;
    unsigned int vUInt;
    long vLong;
    double vDouble;
    float vFloat;
    char vChar;
    bool vBool;
    std::string vString;

    std::string _latest = "null";
    std::map <std::string, bool> _stored = {
        {"int", false},
        {"uint", false},
        {"long", false},
        {"double", false},
        {"float", false},
        {"char", false},
        {"bool", false},
        {"string", false},    
    };

    static std::string intname;
    static std::string uintname;
    static std::string longname;
    static std::string doublename;
    static std::string floatname;
    static std::string charname;
    static std::string boolname;
    static std::string stringname;


    template<class T>
    void set(const T &v, const std::string &vname){
        const void * p = &v;

        if ( vname == intname ) {
            vInt = *static_cast<const int *> (p);
            _latest = "int";
        } else if ( vname == uintname ){
            vUInt = *static_cast<const unsigned int *> (p);
            _latest = "uint";
        } else if ( vname == longname ){
            vLong = *static_cast<const long *> (p);
            _latest = "long";
        } else if ( vname == doublename ){
            vDouble = *static_cast<const double *> (p);
            _latest = "double";
        } else if ( vname == floatname ){
            vFloat = *static_cast<const float *> (p);
            _latest = "float";
        } else if ( vname == charname ){
            vChar = *static_cast<const char *> (p);
            _latest = "char";
        } else if ( vname == boolname ){
            vBool = *static_cast<const bool *> (p);
            _latest = "bool";
        } else if ( vname == stringname ){
            vString = *static_cast<const std::string *> (p);
            _latest = "string";
        } else {
            throw "unsupported type.";
        }

        _stored[_latest] = true;
    };

    public:

    template<class T>
    _cDynamicx operator=(const T &v)
    {    
        std::string thisname = typeid(this).name();
        std::string vname = typeid(v).name();
        if ( vname == thisname ) return *this;
        set(v, vname);
        return *this;
    };

    _cDynamicx(){};
    template<class T>
    _cDynamicx(const T &v){
        std::string vname = typeid(v).name();
        set(v, vname);
    };

    bool get(int &v){
        if ( _stored["int"] == true ){
            v = vInt;
            return true;
        } else return false;
    };

    bool get(unsigned int &v){
        if ( _stored["uint"] == true ){
            v = vUInt;
            return true;
        } else return false;
    };    

    bool get(long &v){
        if ( _stored["long"] == true ){
            v = vLong;
            return true;
        } else return false;
    };

    bool get(double &v){
        if ( _stored["double"] == true ){
            v = vDouble;
            return true;
        } else return false;
    };

    bool get(float &v){
        if ( _stored["float"] == true ){
            v = vFloat;
            return true;
        } else return false;
    };

    bool get(char &v){
        if ( _stored["char"] == true ){
            v = vChar;
            return true;
        } else return false;
    };


    bool get(bool &v){
        if ( _stored["bool"] == true ){
            v = vBool;
            return true;
        } else return false;
    };

    bool get(std::string &v){
        if ( _stored["string"] == true ){
            v = vString;
            return true;
        } else return false;
    };

    int getI(){ return vInt; };
    unsigned int getUI() { return vUInt; };
    long getL() { return vLong; };
    double getD() { return vDouble; };
    float getF() { return vFloat; };
    char getC() { return vChar; };
    bool getB() { return vBool; };
    std::string getS() { return vString; };


    std::string latest() { return _latest; };
    std::map<std::string, bool> stored() { return _stored; };

};

std::string _cDynamicx::intname = typeid(int).name();
std::string _cDynamicx::uintname = typeid(unsigned int).name();
std::string _cDynamicx::longname = typeid(long).name();
std::string _cDynamicx::doublename = typeid(double).name();
std::string _cDynamicx::floatname = typeid(float).name();
std::string _cDynamicx::charname = typeid(char).name();
std::string _cDynamicx::boolname = typeid(bool).name();
std::string _cDynamicx::stringname = typeid(std::string).name();