【问题标题】:Dealing with size of stl containers处理 stl 容器的大小
【发布时间】:2011-10-04 17:51:57
【问题描述】:

我正在重写一个我在学习 STL 之前编写的通用库。它一直使用 C 风格的数组。很多地方都有这样的代码:

unsigned short maxbuffersize; // Maximum possible size of the buffer. Can be set by user.
unsigned short buffersize; // Current size of the buffer.
T *buffer; // The buffer itself.

我做的第一件事就是像这样更改代码:

unsigned short maxbuffersize;
unsigned short buffersize;
std::vector<T> buffer;

然后:

typedef unsigned short BufferSize;
BufferSize maxbuffersize;
BufferSize buffersize;
std::vector<T> buffer;

然后我觉得我做了一件非常糟糕的事情,应该重新考虑我的编码风格。起初,BufferSize 似乎是一个非常糟糕的类型名称,但随后各种奇怪的问题开始出现。如何命名尺寸类型?我应该使用自己的类型还是从std::vector&lt;T&gt;::size_type 继承?我应该缓存容器的大小还是一直使用size()?我是否应该允许用户手动设置容器的最大大小,如果不允许,如何检查溢出?

我知道不可能有千篇一律的方法,因此我想听听其他编码人员和框架供应商使用的策略。我正在开发的库是跨平台的通用库,旨在发布到公共领域并使用数十年。谢谢。

【问题讨论】:

  • 既然你要把它发布到公共领域,我认为你应该给你的程序一个小插件。
  • 它是Source engine console 的克隆,去掉了一些限制(例如只有一个实例)。
  • 王牌,如果你在互联网上建立了一个仓库(比如github)——发个链接
  • 有一个旧版本here。从那以后我重写了主要部分,并计划在它足够完善时发布它。

标签: c++ stl


【解决方案1】:

我认为默认选择应该是去掉buffersizemaxbuffersize 并始终使用buffer.size()buffer.capacity()

我建议不要缓存大小除非你有非常具体的理由这样做,并有分析器运行的硬数据支持。缓存会带来额外的复杂性,并有可能使缓存与真实事物同步。

最后,在您认为有必要进行边界检查的地方,您可以使用buffer.at(i)。如果i 越界,这将引发异常。

【讨论】:

  • 缓冲std::vector 的大小是完全愚蠢的,因为它保证具有恒定的性能(即与元素数量无关)。据我所知,对于任何其他序列容器也是如此(包括std::arraystd::stringstd::valarray
  • @rubenvb 真的吗? for(size_t i=0; i&lt;buffer.size();++i) 是否不必为每次迭代检查缓冲区的大小——程序员可能知道在循环中不会改变的东西。 (我同意 Aix - 应该使用分析器来确定此类检查是否昂贵 - 因为它们可能不是。)。
  • 命名约定怎么样?我认为 BufferSizeType 是一个很好的方法,但是在某些情况下,传递索引的事实对用户来说是不透明的,我不知道有什么好的方法来解决这个问题。
  • @FaTony:我个人的偏好是在用户 API 中使用纯整数类型,而不是为每种可能情况使用单独的 typedef。其他人可能不同意。
  • Tom:这是一个函数调用,是的,但是成员函数本身具有恒定的时间复杂度,即它不会遍历并计算所有元素。随着元素的添加或删除,大小会更新(当然,仅对于 sequance 容器std::list 在这方面完全不同,但不是 OP 所要求的......)跨度>
【解决方案2】:

一般来说,我建议使用迭代器来访问您的数据。当您这样做时,您通常根本不会显式调用容器的大小。这也使您不必同时使用std::vector - 如果您稍后意识到这更适合您的需求,您可以简单地更改为,例如std::list

当您使用迭代器时,对vector.size() 的需求通常会大大减少。 (当您确实需要它时,请使用 buffer.size()buffer.capacity(),如 aix 所说)。

例如:

typedef unsigned short BufferSize;
BufferSize maxbuffersize;
BufferSize buffersize;
std::vector<T> buffer;
for(unsigned short i = 0; i< maxbuffersize;++i)
{
  //do something with buffer[i];
}

变成

struct do_something
{
  void operator()(const T& t) 
  { 
   //do something with buffer[i]
  }
};
std::vector<T> buffer(maxbuffersize);
std::for_each(buffer.begin(), buffer.end(), do_something());

这有点干净。

【讨论】:

    【解决方案3】:

    保持大小对于可能的结构很有用,但对于数组/向量来说有点多余,因为大小保证是最终索引+1。如果您担心跑到最后,那么提到的迭代器方法可以解决这个问题,以及关于比较可能大小等的大多数其他问题;

    使用 API 在标头中定义所有类型及其大小是非常标准的,该 API 为不同的平台和编译器设置它们...查看带有 LONG、ULONG、DWORD 等定义的窗口。旧的“ C" 约定是在它们前面加上一个唯一的名称或首字母,例如 MYAPI_SIZETYPE。它很罗嗦,但避免了任何跨平台混淆或编译器问题。

    【讨论】:

    • 我将所有类型都放入我自己的命名空间中,并且从不使用using 以避免名称冲突。
    猜你喜欢
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-27
    • 2014-11-14
    • 1970-01-01
    • 2021-11-17
    相关资源
    最近更新 更多