在我的应用程序中有一个类(即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 *到数据类型转换。
答案 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();
}
编辑:我看到您正在从数据库加载数据。在这种情况下,我想知道为什么你不只是在需要时从数据库加载按需?
答案 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_t
和boost::variant
。该集仅限于标记,因此没有两个细节具有相同的标记。值可能是
boost_any
detail_value_base
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();