使用仅在构造函数方面有所不同的类有什么好处和缺点?

时间:2013-05-01 20:31:28

标签: c++ oop

我有一个类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 个答案:

答案 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,它是否是构造函数或常规方法都无关紧要。