2

我有一个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>具有不同名称和特殊构造函数的类。什么时候——如果有的话!——这是一个合理的想法吗?哪种方法更好?

4

4 回答 4

3

我相信选项一最有意义,因为它:

  • 有一个非常简单的界面
  • 不创建额外的类
  • 可以通过模板扩展其他“提取”操作

我反对选项二,因为如果您继承自std::vector<int>您说SpecialArrayExtract is-a std::vector<int>,并提供单个操作(构造函数)来创建它。如果您考虑一下,这只是一种与您的第一个替代方案完全相同的复杂方式。

选项三似乎是一个过早的优化(您选择包含而不是继承,就好像您会先发制人地期望底层数据容器发生变化),它比备选方案二更令人费解。

至于选项四(建议在答案中),我认为类接口应该尽可能少地保留实例方法,而是提供非成员函数(参考这篇文章了解原理)。

如果您的需求在未来发生变化(例如,您决定另一种容器或数据类型更好地满足您的需求),您可以通过模板修改函数。这将为您提供拥有单独课程的所有优势,但不会带来开销。并且相同的操作(具有完全相同的名称)可以应用于 ContainerTypes(即,std::vector<int>std::array<int, 12>,或其他)和 ArrayTypes(SpecialArraySparseSpecialArray,或其他)的任何可能组合:

template<typename ContainerType, typename ArrayType>
ContainerType extract(const ArrayType& array, ...)

最后,您可以创建一个单独的algorithms名称空间,其中包含所有作用于ArrayType数据的函数(例如,SpecialArray)。这将使记录和维护您的代码变得非常简单:您的数据类型的所有通用操作都存在于 中,对于这种操作,algorithms::命名空间是比具有许多静态方法的类更自然的聚合器。

于 2013-05-01T20:46:33.633 回答
2
template < typename OutIt >
void extract(SpecialArray const&, OutIt dest_begin);

这适用于任何容器,用于存储结果的容器的任何类型的预分配。

如果您连续编写元素(à la push_back),您可以back_insert_iterator在必要时使用 a 来调整容器的大小,但这将调整大小,例如如果不够大,则向量会导致一些性能影响。否则,您可能想要使用随机访问迭代器(不更改函数模板签名,可能将名称从更改OutItRAIt)。

您可能希望添加一个(成员)函数来获取预分配所需的大小(如果它必须访问私有数据成员,则为成员)。

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 的论证,并同意对选项的判断。

于 2013-05-01T20:59:21.557 回答
1

选项 4

为类 SpecialArray 实现公共成员函数:

std::vector<int> extract(...);
于 2013-05-01T20:33:55.050 回答
0

我个人只是在 SpecialArray 类中实现一个 ToVector(...) 方法。如果您无权访问源代码(我相信您可以),您可以将该方法添加为该类的扩展。

据我了解,您的问题是您在内存中确实有一个 SpecialArray,并且您想将其转换为 std::vector。如果这两个类都在内存中隔离(即没有缓存来提高转换性能),那么您只需要一种方法来获取 SpecialArray 并将其转换为 std::vector。只要 SpecialArray 是通过引用传递的,它是构造函数还是常规方法都没有关系。

于 2013-05-01T20:58:37.513 回答