在过去 2 个月的饮食、睡眠和呼吸图上度过,我有一个建议。尽可能让地图分配它自己的数据。由于您在此处强调的原因,它更清洁了。
还有一些微妙的优势,例如如果您将数据从文件或套接字复制到地图的数据,只要节点存在,数据存储就会存在,因为当地图调用 malloc() 分配节点时,它会分配内存对于密钥和数据。(又名 map[key].first 和 map[key].second)
这允许您使用赋值运算符而不是 memcpy(),并且需要对 malloc() 的调用减少 1 次 - 您所做的调用。
IC_CDR CDR, *pThisCDRLeafData; // a large struct{}
while(1 == fread(CDR, sizeof(CDR), 1, fp)) {
if(feof(fp)) {
printf("\nfread() failure in %s at line %i", __FILE__, __LINE__);
}
cdrMap[CDR.iGUID] = CDR; // no need for a malloc() and memcpy() here
pThisCDRLeafData = &cdrMap[CDR.iGUID]; // pointer to tree node's data
需要注意的一些注意事项值得在这里指出。
- 不要在添加树节点的代码行中调用 malloc() 或 new,因为您对 malloc() 的调用将返回一个指针,然后地图对 malloc() 的调用已分配一个位置来保存 malloc() 的返回.
- 在调试模式下,尝试释放()你的内存时会遇到类似的问题。这两个对我来说似乎都是编译器问题,但至少在 MSVC 2012 中,它们存在并且是一个严重的问题。
考虑一下在哪里“锚定”您的地图。IE:声明它们的地方。您不希望它们错误地超出范围。main{} 始终是安全的。
INT _tmain(INT argc, char* argv[]) {
IC_CDR CDR, *pThisCDRLeafData=NULL;
CDR_MAP cdrMap;
CUST_MAP custMap;
KCI_MAP kciMap;
我很幸运,很高兴有一个关键映射分配一个结构作为节点数据,并让该结构“锚定”一个映射。虽然 C++ 已经放弃了匿名结构(一个必须撤销的可怕决定),但作为第一个结构成员的映射就像匿名结构一样工作。非常光滑和干净,尺寸效应为零。在函数调用中传递指向叶拥有的结构的指针,或按值传递结构的副本,两者都工作得很好。强烈推荐。
- 您可以捕获 .insert 的返回值,以确定它是否在该键上找到了现有节点,或者创建了一个新节点。(代码见#12)使用下标表示法不允许这样做。选择 .insert 并坚持下去可能会更好,特别是因为 [] 符号不适用于多图。(这样做是没有意义的,因为没有“a”键,而是多重映射中具有相同值的一系列键)
- 您可以而且应该捕获 .erase 和 .empty() 的返回值(是的,有些东西是函数,需要 () 而有些,比如 .erase,则不需要)
- 您可以使用 .first 和 .second 获取任何映射节点的键值和数据值,按照惯例,所有映射都使用它们分别返回键和数据
为自己节省大量的混乱和打字,并为您的地图使用 typedef,就像这样。
typedef map<ULLNG, IC_CDR> CDR_MAP;
typedef map<ULLNG, pIC_CDR> CALL_MAP;
typedef struct {
CALL_MAP callMap;
ULNG Knt;
DBL BurnRateSec;
DBL DeciCents;
ULLNG tThen;
DBL OldKCIKey;
} CUST_SUM, *pCUST_SUM;
typedef map<ULNG,CUST_SUM> CUST_MAP, CUST_MAP;
typedef map<DBL,pCUST_SUM> KCI_MAP;
使用 typedef 和 & 运算符传递对映射的引用,如
ULNG DestroyCustomer_callMap(CUST_SUM Summary, CDR_MAP& cdrMap, KCI_MAP& kciMap)
对迭代器使用“auto”变量类型。编译器将从 for() 循环体的其余部分中指定的类型确定要使用哪种映射 typedef。它是如此的干净,几乎是魔法!
for(auto itr = Summary.callMap.begin(); itr!= Summary.callMap.end(); ++itr) {
定义一些清单常量以使 .erase 和 .empty() 的返回更有意义。
if(ERASE_SUCCESSFUL == cdrMap.erase (itr->second->iGUID)) {
鉴于“智能指针”实际上只是保持引用计数,请记住,您始终可以保持自己的引用计数,这可能以一种更清晰、更明显的方式。将它与上面的#5 和#10 结合起来,您可以编写一些像这样的漂亮干净的代码。
#define Pear(x,y) std::make_pair(x,y) // some macro magic
auto res = pSumStruct->callMap.insert(Pear(pCDR->iGUID,pCDR));
if ( ! res.second ) {
pCDR->RefKnt=2;
} else {
pCDR->RefKnt=1;
pSumStruct->Knt += 1;
}
使用指针挂在为自己分配所有内容的映射节点上,IE:没有指向用户 malloc()ed 对象的用户指针,运行良好,可能更有效,并且可用于改变节点的数据而没有副作用在我的经验中。
在同一主题上,可以非常有效地使用这样的指针来保存节点的状态,pThisCDRLeafData
如上所示。将其传递给改变/更改特定节点数据的函数比传递对映射的引用更干净,并且返回节点所需的键pThisCDRLeafData
指向。
迭代器不是魔术。当您在地图上导航以获取值时,它们既昂贵又缓慢。对于包含一百万个值的映射,您可以每秒大约 2000 万次基于键读取节点。使用迭代器可能会慢 1000 倍。
我认为现在涵盖了它。如果有任何变化或有其他见解要分享,将会更新。我特别喜欢将 STL 与 C 代码一起使用。IE:在任何地方都看不到一堂课。它们只是在我工作的环境中没有意义,这不是问题。祝你好运。