請讀者先看這篇文章,【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的時候,無法在編譯期知道哪個指針有效。