std :: vectors应该广泛用于嵌入式系统吗?

时间:2013-12-30 14:22:32

标签: c++ c++11 vector stl embedded

为具有有限CPU和内存资源的嵌入式系统编写C ++代码时,常见的经验法则是实例化堆栈中的对象,并避免使用堆,除非确实有必要。当然这样做有很多已知的好处,但是随着STL和人们推荐std :: vectors作为一种有效的数据结构的出现,它是否违反了我提到的经验法则,因为向量将使用堆?

示例:在过去,人们会声明具有已满足用量的已知大小的静态数组。如今,人们只会使用矢量。

我对这种转换并不是很满意,因为矢量无法分配所需的内存(提醒:这适用于内存有限的嵌入式系统)。在堆栈中使用已知大小的数组可确保在编译期间有分配空间。

调用reserve()有点帮助,但这是在运行时完成的。

那么,这是引起关注的原因,还是我只是偏执狂?使用这些向量肯定要容易得多,但对于嵌入式环境,它可能不是一个好主意吗?

注意:这不是关于动态与固定数组的关系,而是关于如何在内存中分配数据的更多内容,这对我的环境来说是一个大问题。举个例子,有些人会这样做:假设数组可以在1到10个元素之间增长或缩小。有些人会在堆栈中创建一个覆盖此大小的数组,并根据当前大小终止NULL。这样就避免了碎片,并且在编译期间保证了分配。但是,切换到vector会使它更清晰,但代价是使用堆,并且如果分配失败可能不得不处理异常。这就是我所关注的。

5 个答案:

答案 0 :(得分:8)

我相信您忘记了STL容器的一个非常重要的属性: allocators

STL容器(无论是vector还是其他)从其分配器获取所有内存(除了可以使用sizeof检查的非常基本的堆栈占用空间)。因此,它非常适合在嵌入式开发中提供专用的分配器:

  • 将从预先保留的内存区域分配
  • 将绑定最大资源消耗以防止OOM
  • ...

随着C ++ 11的出现,您甚至可以使用有状态分配器,以便单个分配器类型可以指向不同的内存池。

因此,std::vector,甚至std::setstd::map的使用与预分配策略不相容;请记住,除了std::vector之外,其他STL容器通常都有一些每项开销,在确定它们应该使用的内存区域时必须考虑到这一点。

答案 1 :(得分:4)

“取决于”在这里可能是轻描淡写。通过使用带向量的保留,您可以有效地为数据分配空间并防止不必要的副本。如果编译器足够好,则向量数据结构本身将归结为具有大小的堆分配数组。

另外,请注意我说。如果您希望将数据分配给堆栈,那么您会遇到固定大小的数组(包括std :: array)。

它还在很大程度上取决于编译器如何将向量放在一起。较旧的编译器可能(非常强调可能)效率较低。这就是为什么对于嵌入式系统,在对“好”和“坏”使用的内容进行全面概括之前,您确实需要了解架构和编译器。

答案 2 :(得分:2)

std::vector应该用于动态调整大小的数组。如果您在编译时知道大小,则可以使用std::array代替。

如果您知道数组的唯一近似大小,自C ++ 14 ,您可以使用std::dynarray。此数组具有固定大小,在对象生存期内无法更改。当使用没有分配器的std::dynarray时,可以进行额外的优化,可能不会调用operator new并且将使用基于堆栈的分配。

答案 3 :(得分:2)

  

在堆栈中使用已知大小的数组可确保在编译期间有空间进行分配。

错误的假设。

当你有很多调用时(甚至不是递归的,而是一个具有很多级别的堆栈),你不能确定你的堆栈有足够的空间来容纳必须创建的对象。

有基本的检查(如果你的对象的大小至少超过size_t),但我甚至认为它们不是标准强制要求。

A - 有些过分 - 例如:

#include <iostream>

template <unsigned long size>
struct BigObject
{
    unsigned long array[size * size];
    BigObject<size - 1> object;
};


template <>
struct BigObject<0>
{
    unsigned long array[1]; 
};

BigObject<900>& recurse(BigObject<900>& object1, unsigned long n)
{
    if (n == 0)
    {
        return object1;
    }

    BigObject<900> object;

    return recurse(object, n);
}

int main(int argc, char const *argv[])
{
    BigObject<900> object;
    recurse(object, 20);

    std::cout << sizeof(object) << std::endl;
    return 0;
}

http://ideone.com/pkHo43

确实崩溃了。 这不是一个特例。我在32位面向桌面的应用程序中遇到了问题(因此没有任何内存限制)在堆栈上分配太大的数组。

答案 4 :(得分:1)

当使用足够的优化标志进行编译时,std::vector只是一个指针和两个size_t。现在,size_t的内存在您事先了解向量的大小时就会浪费,而且永远不会改变。

我会说:

正如评论中所提到的,您可以使用大多数功能,例如:通过使用begin / end迭代器的指针,在固定大小的数组上<algorithms>

例如:

int array[8] = { 1,2,3,4,5,6,7,8 };
std::random_shuffle(array, array+8);