在C ++中,从性能和数据完整性的角度来看,我有哪些替代方案来展示集合?
我的问题是我想将一个内部数据列表返回给调用者,但我不想生成副本。 Thant让我返回对列表的引用,或指向列表的指针。但是,我并不想让调用者更改数据,我只是想让它读取数据。
答案 0 :(得分:7)
很多时候调用者想要访问只是迭代集合。从Ruby的书中拿出一页,让迭代成为你班级的私人方面。
#include <algorithm>
#include <boost/function.hpp>
class Blah
{
public:
void for_each_data(const std::function<void(const mydata&)>& f) const
{
std::for_each(myPreciousData.begin(), myPreciousData.end(), f);
}
private:
typedef std::vector<mydata> mydata_collection;
mydata_collection myPreciousData;
};
使用这种方法,您不会暴露任何关于内部的内容,即您甚至拥有集合。
答案 1 :(得分:5)
RichQ's answer是一种合理的技术。
如果您正在使用未按序数值编制索引的集合...或者认为您可能需要在不久的将来某个时间点......那么您可能需要考虑公开您自己的迭代器类型以及相关的begin()
/ end()
方法:
class Blah
{
public:
typedef std::vector<mydata> mydata_collection;
typedef myDataCollection::const_iterator mydata_const_iterator;
// ...
mydata_const_iterator data_begin() const
{ return myPreciousData.begin(); }
mydata_const_iterator data_end() const
{ return myPreciousData.end(); }
private:
mydata_collection myPreciousData;
};
...然后您可以以正常方式使用:
Blah blah;
for (Blah::mydata_const_iterator itr = blah.data_begin();
itr != blah.data_end();
++itr)
{
// ...
}
答案 2 :(得分:3)
也许是这样的?
const std::vector<mydata>& getData()
{
return _myPrivateData;
}
这里的好处是它非常,非常简单,并且像在C ++中一样安全。你可以像RobQ建议的那样投出这个,但如果你没有复制的话,那么你无法阻止某人这样做。在这里,您必须使用const_cast
,如果您正在寻找它,这很容易被发现。
答案 3 :(得分:2)
使用const引用或共享指针只有在底层集合的内容不随时间变化时才有用。
考虑你的设计。调用者是否真的需要查看内部数组?你可以重组代码,以便调用者告诉对象如何处理数组?例如,如果调用者打算搜索数组,那么所有者对象可以执行吗?
您可以将结果向量的引用传递给函数。在某些编译器上可能会导致代码速度略快。
我建议先尝试重新设计,然后再使用干净的解决方案,优化性能第三(如有必要)。
答案 4 :(得分:2)
@ Shog9和@ RichQ解决方案的一个优点是它们将客户端与集合实现分离。
如果您决定将收藏类型更改为其他类型,则您的客户仍然可以使用。
答案 5 :(得分:1)
您想要的是只读访问而不复制整个数据块。你有几个选择。
首先,你可以向你的数据容器返回一个const引用,如上所述:
const std::vector<T>& getData() { return mData; }
这具有具体的缺点:您无法在不更改类的界面的情况下更改内部存储数据的方式。
其次,您可以返回指向实际数据的常量指针:
const T* getDataAt(size_t index)
{
return &mData[index];
}
这有点好,但还要求您提供getNumItems调用,并防止越界索引。此外,你的指针的常量很容易丢弃,你的数据现在是可读写的。
另一个选择是提供一对迭代器,这有点复杂。这与指针具有相同的优点,并且(不一定)需要提供getNumItems调用,并且还需要更多工作来去除其常量的迭代器。
管理此问题的最简单方法可能是使用Boost Range:
typedef vector<T>::const_iterator range_iterator_type;
boost::iterator_range< range_iterator_type >& getDataRange()
{
return boost::iterator_range(mData.begin(), mData.end());
}
这具有范围可组合,可过滤等优点,正如您在website上看到的那样。
答案 6 :(得分:0)
使用const是一个合理的选择。 您可能还希望查看boost C ++库的共享指针实现。它提供了指针的优点,即您可能需要将共享指针返回到引用不允许的“null”。
http://www.boost.org/doc/libs/1_36_0/libs/smart_ptr/smart_ptr.htm
在您的情况下,您将使共享指针的类型为const以禁止写入。
答案 7 :(得分:0)
如果你有一个std::list
普通的旧数据(.NET会称之为'值类型'),那么返回一个对这个列表的const引用就没问题了(忽略像const_cast
这样的邪恶事物)
如果你有一个std::list
指针(或boost::shared_ptr
),那么这只会阻止你修改集合,而不是集合中的项。我的C ++太生疏了,无法在这一点上告诉你答案: - (
答案 8 :(得分:0)
以下两篇文章详细阐述了封装容器类所涉及的一些问题和需求。虽然它们没有提供完整的解决方案,但它们基本上导致了与Shog9相同的方法。
第1部分:Encapsulation and Vampires
第2部分(现在需要免费注册才能阅读本文):Train Wreck Spotting
作者:Kevlin Henney
答案 9 :(得分:0)
我建议使用EnumChildWindows的回调。您必须找到一些方法来阻止用户更改您的数据。也许使用const
指针/引用。
另一方面,您可以将每个元素的副本传递给回调函数,每次都覆盖副本。 (你不想生成整个集合的副本。我只建议一次复制一个元素。这不应该花费太多时间/内存。)
MyClass tmp;
for(int i = 0; i < n; i++){
tmp = elements[i];
callback(tmp);
}