【问题标题】:Is it a bad idea to replace POD C-style array with std::valarray?用 std::valarray 替换 POD C 样式数组是不是一个坏主意?
【发布时间】:2015-01-30 14:13:59
【问题描述】:

我正在使用一个写得不好并且有很多内存泄漏的代码库。

它使用了很多包含原始指针的结构,主要用作动态数组。

虽然结构体经常在函数之间传递,但这些指针的分配和释放是随机放置的,不容易跟踪/推理/理解。

我将其中一些更改为类,并且这些指针将由类本身进行 RAIIed。它们运行良好,而且看起来并不难看,只是我禁止了这些类的复制构造和复制分配,只是因为我不想花时间实现它们。

现在我在想,我是不是在重新发明轮子?为什么不用 std:array 或 std::valarray 替换 C 样式的数组?

我更喜欢 std::valarray 因为它使用堆内存和 RAIIed。并且 std::array 在我的开发环境中(还)不可用。

Edit1:std::array 的另一个优点是这些动态数组中的大多数都是 POD(主要是 int16_t、int32_t 和 float)数组,而数值 API 可以让生活更轻松。

在开始之前我有什么需要注意的吗?

我能想到的一点是,可能没有一种简单的方法可以将 std::valarray 或 std::array 转换回 C 风格的数组,并且我们的部分代码确实使用了指针算法并且需要呈现数据作为普通的 C 样式数组。

还有什么?

编辑 2

我最近遇到了this questionstd::valarray 的一个非常糟糕的事情是它直到 C++11 才可以安全地复制分配。

正如该答案中所引用的,在 C++03 及更早版本中,如果源和目标的大小不同,则为 UB。

【问题讨论】:

  • 除非我错过了它,否则我很惊讶std::valarray 没有像std::vector 这样的.data() 来在您需要C 样式数组时公开底层原始数组。
  • 根据您的描述,似乎 std::vector 应该是合适的。有什么特别值得您考虑 std::valarray 的吗?
  • std::valarray 是/曾经是用于 LAPACKish 应用程序的;它很少使用,可能不太适合这里。 std::vector 是动态数组的常用容器,您可以使用 C++11 之前的 &vec[0] 获取指向其数据的指针(当 .data() 尚不可用时)。
  • @nos std::valarray 的一个优点是它的数值 API 非常接近 numpy 和 matlab。而且我使用的大部分数组都是POD数组。
  • @Cyber​​:valarray 的目标之一是确保防止别名,这相当直接地转化为禁止访问任何指向实际数据的指针。

标签: c++ c++11 stl valarray


【解决方案1】:

C 样式数组的标准替换是std::vectorstd::valarray 是一些“奇怪”的数学向量,用于进行类似数字计算的事情。它并不是真正设计用于存储任意对象的数组。

话虽如此,使用std::vector 很可能是一个非常的好主意。它将修复您的泄漏、使用堆、可调整大小、具有出色的异常安全性等等。

它还保证数据存储在一个连续的内存块中。您可以使用 data() 成员函数获取指向该块的指针,或者,如果您是 C++11 之前的版本,则可以使用 &v[0] 获取非空向量 v。然后,您可以像往常一样使用它进行指针业务。

【讨论】:

  • 不要忘记使用std::vector,您也无需执行任何操作即可获得复制语义。不再需要delete 复制构造函数和赋值运算符。
  • @5gon12eder 是的,我也想过这个问题。但从长远来看,出于性能原因,我仍然会禁止它们。无论如何,深度复制大型数值数组并不是一个好习惯,这就是 MATLAB 速度慢的(原因之一)。
  • @user3528438 不复制(几乎)总是比复制快,但如果您必须复制,使用std::vector 中高度优化的实现会派上用场。
【解决方案2】:

std::unique_ptr<int[]> 接近于替代拥有的int*。它有一个很好的特性,它不会隐式复制自己,但会隐式移动。

复制操作会产生编译时错误,而不是运行时效率低下。

与拥有int* 相比,它也几乎没有运行时开销,除了在销毁时进行空检查。它使用的空间不超过int*

std::vector<int> 存储 3 个指针和隐式副本(这可能很昂贵,并且与您现有的代码行为不匹配)。

我会从std::unique_ptr<int[]> 作为第一遍开始并让它工作。在我决定智能缓冲区管理值得之后,我可能会将一些代码转换为 std::vector<int>

实际上,作为第一遍,我会寻找 memcpymemset 和类似的函数,并确保它们没有在我开始添加 RAII 成员之前对相关结构进行操作。

std::unique_ptr<int[]> 表示默认为结构创建的析构函数将为您执行 RAII 清理,而无需编写任何新代码。

【讨论】:

  • 您能进一步解释一下为什么要寻找 memcpy 和 memset 吗?是不是因为 memcpy(struct *to, struct *from, sizeof(struct)) struct 转换为 RAII 类时非常不安全?
  • @user3528438 是的。 memset 清除,memcpy 复制等——所有这些都是 C 风格代码中常见的微优化,将对 RAII 类(在实践中)和未定义行为(在理论上)造成致命的破坏。
【解决方案3】:

我更喜欢 std::vector 作为 c 样式数组的替代品。您可以通过.data() 直接访问底层数据(类似于裸指针):

返回指向作为元素存储的底层数组的指针。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    • 2011-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多