請讀者先看這篇文章,【C++模版之旅】項目中一次活用C++模板(traits)的經歷。 對於此篇文章提出的問題,我給出一個新的思路。
talking is cheap,show me the code.
class ExportData
{
union
{
string * sp;
long* lp;
double* dp;
void* vp;
};
enum my_type {SP,LP,DP} types;
static unordered_map<type_index,my_type> typeMap;
public:
template <typename T> ExportData(T t)
{
if(typeMap.find(typeid(t))==typeMap.end())
assert(false);
vp=new T(t);
types= typeMap[typeid(T)];
}
template <typename T> void setData(T t)
{
if(typeMap.find(typeid(t))==typeMap.end())
assert(false);
switch(types)
{
case SP:
delete sp;
break;
case DP:
delete dp;
break;
case LP:
delete lp;
break;
}
vp=new T(t);
types=typeMap[typeid(T)];
}
template <typename T> void getData(T& t)
{
if(typeMap[typeid(T)]!=types) assert(false);
t=*(static_cast<T*>(vp));
}
};
unordered_map<type_index,ExportData::my_type> ExportData::typeMap
{
{typeid(string),ExportData::my_type::SP},
{typeid(long),ExportData::my_type::LP},
{typeid(double),ExportData::my_type::DP},
};
1. ExportData需要僅支持整型(long),浮點型(double),字符串(string)以及二進制(void*, size)4種類型的操作。(我並沒有考慮二進制) 2. ExportData需要考慮結構的尺寸,盡量減少空間冗余(我使用聯合體,保存各種類型數據的指針) 3. 即使對以上4種不同數據類型進行操作,還是希望在從ExportData中Get或Set真實數據時,使用的方法能統一(方法顯然是統一的,因為使用的是模版) 4. 當調用者嘗試使用了以上4種類型以外的數據類型時,能通過返回錯誤讓調用方知道類型不匹配(為了方便演示,試圖使用其他類型都會導致斷言失敗,終止運行)
class DeleteLong
{
public:
void operator()(void *p)
{
delete static_cast<long*>(p);
}
};
class DeleteString
{
public:
void operator()(void *p)
{
delete static_cast<string*>(p);
}
};
class DeleteDouble
{
public:
void operator()(void *p)
{
delete static_cast<double*>(p);
}
};
class ExportData
{
union
{
string * sp;
long* lp;
double* dp;
void* vp;
};
enum my_type {SP,LP,DP} types;//change it to object.
static unordered_map<type_index,my_type> typeMap;
static vector<function<void(void*)>> deleters;
public:
template <typename T> ExportData(T t)
{
static_assert(is_same<T,string>::value||is_same<T,double>::value||is_same<T,long>::value,"never support!");//感謝崔方方
// if(typeMap.find(typeid(t))==typeMap.end())
// assert(false);
vp=new T(t);
types= typeMap[typeid(T)];
}
template <typename T> void setData(T t)
{
if(typeMap.find(typeid(t))==typeMap.end())
assert(false);
(deleters[types])(vp);
vp=new T(t);
types=typeMap[typeid(T)];
}
template <typename T> void getData(T& t)
{
if(typeMap[typeid(T)]!=types) assert(false);
t=*(static_cast<T*>(vp));
}
//這裡可以改成重載,void getData(long& t){...} void getData(sting& t){....} void getData(double& t){...}調用其他類型則編譯錯誤
};
unordered_map<type_index,ExportData::my_type> ExportData::typeMap
{
{typeid(string),ExportData::my_type::SP},
{typeid(long),ExportData::my_type::LP},
{typeid(double),ExportData::my_type::DP},
};
vector<function<void(void*)>> ExportData::deleters {DeleteString(),DeleteLong(),DeleteDouble(),};
這裡是測試代碼:
int main()
{
long i=5;
long j=0;
string s="Hello";
string ss;
ExportData p(i);
p.setData(++i);
p.getData(j);
p.setData(s);
p.getData(ss);
cout<<j<<endl;
cout<<ss<<endl;
return 0;
}
這是一個精簡版,使用重載:
class ExportData
{
union
{
string * sp;
long* lp;
double* dp;
};
public:
ExportData(long t)
{
lp=new long(t);
}
ExportData(double t)
{
dp=new double(t);
}
ExportData(string t)
{
sp=new string(t);
}
void setData(long t)
{
*lp=t;
}
void setData(double t)
{
*dp=t;
}
void setData(string t)
{
*sp=t;
}
void getData(long& t)
{
t=*lp;
}
void getData(double& t)
{
t=*dp;
}
void getData(string& t)
{
t=*sp;
}
//1.析構函數需要解決內存洩露問題 2.如當前指針指向double,setData函數傳入string,會發生內存錯誤。
};
這個版本存在兩個嚴重的問題,1.析構函數需要解決內存洩露問題 2.如當前指針指向double,setData函數傳入string,會發生內存錯誤。
我覺得第二個錯誤,沒辦法在編譯期阻止用戶編譯,因為在setData的時候,無法在編譯期知道哪個指針有效。