我花了一些时间寻找答案,但没有发现任何令人满意的事情。
对于一些经验丰富的C ++人员如何解决这类问题感兴趣,因为现在我正在做一些与生产相关的编码而不是原型设计。
假设你有一个类已经说出了一个包含大量数据的unordered_map(hashmap),比如500Mb。您希望编写一个访问器,以有效的方式返回该数据的某些子集。
采取以下措施,其中BigData是一个存储适量数据的类。
Class A
{
private:
unordered_map<string, BigData> m_map; // lots of data
public:
vector<BigData> get10BestItems()
{
vector<BigData> results;
for ( ........ // iterate over m_map and add 10 best items to results
// ...
return results;
}
};
访问器get10BestItems在此代码中效率不高,因为它首先将项目复制到结果向量,然后在返回函数时复制结果向量(从函数堆栈复制)。
由于各种原因,你不能在c__中有一个引用向量,这是明显的答案:
vector<BigData&> results; // vector can't contain references.
您可以在堆上创建结果向量并传递对它的引用:
vector<BigData>& get10BestItems() // returns a reference to the vector
{
vector<BigData> results = new vector<BigData>; // generate on heap
for ( ........ // iterate over m_map and add 10 best items to results
// ...
return results; // can return the reference
}
但是如果你不小心的话,你会遇到内存泄漏问题。它也很慢(堆内存),仍然将数据从地图复制到矢量。
所以我们可以回顾一下c风格的编码,只使用指针:
vector<BigData*> get10BestItems() // returns a vector of pointers
{
vector<BigData*> results ; // vectors of pointers
for ( ........ // iterate over m_map and add 10 best items to results
// ...
return results;
}
但大多数消息来源表示除非绝对必要,否则不要使用指针。有一些选项可以使用smart_pointers和boost ptr_vector,但我宁愿尽可能避免这些。
我不认为地图会是静态的,所以我不太担心坏指针。只有一个问题,如果代码必须是差异来处理指针。从风格上来说,这并不令人愉快:
const BigData& getTheBestItem() // returns a const reference
{
string bestID;
for ( ........ // iterate over m_map, find bestID
// ...
return m_map[bestID] ; // return a referencr to the best item
}
vector<BigData*> get10BestItems() // returns a vector of pointers
{
vector<BigData*> results ; // vectors of pointers
for_each ........ // iterate over m_map and add 10 best items to results
// ...
return results;
};
例如,如果您想要单个项目,则很容易返回参考。
最后选项是简单地将Hash-map公开并返回一个键向量(在本例中为字符串):
Class A
{
public:
unordered_map<string, BigData> m_map; // lots of data
vector<string> get10BestItemKeys()
{
vector<string> results;
for (........ // iterate over m_map and add 10 best KEYS to results
// ...
return results;
}
};
A aTest;
... // load data to map
vector <string> best10 = aTest.get10BestItemKeys();
for ( .... // iterate over all KEYs in best10
{
aTest.m_map.find(KEY); // do something with item.
// ...
}
什么是最佳解决方案?速度很重要,但我希望易于开发和安全的编程实践。
答案 0 :(得分:3)
如果地图是常数,我会使用指针向量。如果要避免更改数据,可以随时返回const指针。
参考文献非常适合它们的工作,但我们仍然有指针(对我而言,这将属于“必要”类别)。
答案 1 :(得分:2)
我会做类似以下的事情:
Class A
{
private:
unordered_map<string, BigData> m_map; // lots of data
vector<BigData*> best10;
public:
A()
: best10(10)
{
// Other constructor stuff
}
const vector<BigData*>& get10BestItems()
{
// Set best10[0] through best10[9] with the pointers to the best 10
return best10;
}
};
请注意以下几点:
每次都不会重新分配向量并将其作为常量引用返回,因此在您调用get10BestItems
时不会分配或复制任何内容。
在这种情况下,指针很好。您阅读的有关避免指针的内容可能与堆分配有关,在这种情况下,std::unique_ptr
或std::shared_ptr
现在是首选。
答案 2 :(得分:1)
这听起来像boost::ref
给我的工作。只需稍微更改原始代码:
typedef std::vector<boost::ref<BigData> > BestItems;
BestItems get10BestItems()
{
BestItems results;
for ( ........ // iterate over m_map and add 10 best items to results
// ...
return results;
}
现在,您理论上只返回对返回向量中每个项目的引用,使其复制小而且便宜(如果编译器无法完全优化返回副本)。
答案 3 :(得分:0)
我经常使用boost::range,我发现它在很多情况下非常宝贵,特别是你所描述的情况。
你可以保留范围对象并迭代它等等。
但是我应该提一下,如果你在获得范围和使用它之间添加/删除对象时我不知道会发生什么,所以你可能想在使用它之前检查它。