我有一个类SpecialArray
,可以像标准的二维数组一样访问它;即A[i][j]
。经常需要从该对象中提取int
的一维数组。提取的细节取决于其他传递的参数,并不重要;我有兴趣设计操作界面。
完成此任务的以下方法有哪些优点和缺点?
选项1
我们定义一个函数:
std::vector<int> extract(const SpecialArray &A, ...)
其中...
指的是确定一维数组内容的其他参数,并将其用作:
std::vector<int> output = extract(A,...);
选项2
我们创建一个继承自std :: vector并构造自身的类:
class SpecialArrayExtract : public std::vector<int> {
public:
SpecialArrayExtract(const SpecialArray &A, ...);
};
其中构造函数(或等效的init
函数)使用...
输入用适当的数据填充*this
,我们将其用作:
SpecialArrayExtract output(A,...);
选项3
我们完全按照选项2进行操作,但不从std::vector<int>
继承,而是让private
成员std::vector<int>
根据需要公开接口:
class SpecialArrayExtract {
private:
std::vector<int> m_data;
public:
SpecialArrayExtract(const SpecialArray &A, ...);
[Wrapper functions for std::vector<int> here.]
};
评论
应用程序是高性能的,但在选项1中使用RVO,我假设这些应该都是等效的。
将这个重新绑定到标题中,我试图了解的是,选项2和3都定义了基本上只有std::vector<int>
的类,它们具有不同的名称和特殊的构造函数。什么时候 - 如果有! - 这是一个合理的想法,这是更好的方法吗?
答案 0 :(得分:3)
我认为选项一最有意义,因为它:
我反对备选方案2,因为如果你继承std::vector<int>
,你说SpecialArrayExtract
是-a std::vector<int>
,并提供单一操作(构造函数)来创建它。如果您考虑一下,那么与您的第一个选择完全相同只是一种复杂的方式。
选项三似乎是一个不成熟的优化(你选择包含而不是继承,好像你会先发制人地期望底层数据容器改变),它比其他两个更加卷积。
对于选项四(在答案中建议),我认为类接口应该尽可能少地保留实例方法,而是提供非成员函数(参考this article来学习基本原理)
如果您的需求将来发生变化(例如,您决定另一个容器或数据类型可以更好地满足您的需求),您可以通过模板修改该功能。这将为您提供具有单独类的所有优点,但不会产生开销。并且相同的操作(具有完全相同的名称)可以应用于任何可能的ContainerTypes组合(即std::vector<int>
或std::array<int, 12>
或其他)和ArrayTypes(SpecialArray
或{{ 1}},或其他):
SparseSpecialArray
最后,您可以创建一个单独的template<typename ContainerType, typename ArrayType>
ContainerType extract(const ArrayType& array, ...)
命名空间,其中包含对algorithms
数据起作用的所有函数(例如ArrayType
)。这样可以非常直接地记录和维护代码:数据类型的所有通用操作都存在于SpecialArray
中,而命名空间对于这种操作来说是一个更自然的聚合器。比方说,一个有很多静态方法的类。
答案 1 :(得分:2)
template < typename OutIt >
void extract(SpecialArray const&, OutIt dest_begin);
这适用于任何容器,用于存储结果的容器的任何预分配。
如果您连续编写元素(例如push_back
),则可以使用back_insert_iterator
来调整容器大小(如果有必要),但这会调整大小,例如:矢量如果不够大,会导致一些性能影响。否则,您可能希望使用随机访问迭代器(不会更改函数模板签名,可能会将名称从OutIt
更改为RAIt
)。
您可能希望添加(成员)函数以获得预分配所需的大小(如果必须访问私有数据成员,则为成员)。
std::size_t extract_size() const;
示例:
SpecialArray my_special;
constexpr std::size_t len = 100;
int dest_ra[len];
std::array<int, len> dest_a;
std::vector<int> dest_v;
std::list<int> dest_l;
if( len >= my_special.extract_length() )
{
extract( my_special, std::begin(dest_ra) );
extract( my_special, std::begin(dest_a) );
}
// using `push_back`:
dest_v.reserve( my_special.extract_length() ); // not necessary
extract( my_special, std::back_inserter(dest_v) );
extract( my_special, std::back_inserter(dest_l) );
// if random access is required, also a bit faster(*):
dest_v.resize( my_special.extract_length() );
extract( my_special, std::begin(dest_v) );
// not possible for the list
(*)back_insert_iterator
必须进行范围检查才能放大矢量。如果使用普通迭代器,则不进行范围检查。
我喜欢Arrieta的论证并同意判断选项。
答案 2 :(得分:1)
选项4
为SpecialArray类实现公共成员函数:
std::vector<int> extract(...);
答案 3 :(得分:0)
我个人只是在SpecialArray类中实现一个ToVector(...)方法。如果您无法访问源代码(我相信您这样做),您可以将该方法添加为该类的扩展名。
据我所知,你的问题是你在内存中有一个SpecialArray,你想将它转换为std :: vector。如果这两个类都在内存中被隔离(即没有适当的缓存来提高转换性能),那么你只需要一个方法来获取SpecialArray并将其转换为std :: vector。只要通过引用传递SpecialArray,它是否是构造函数或常规方法都无关紧要。