【问题标题】:Declaring arrays in C++在 C++ 中声明数组
【发布时间】:2017-04-07 23:33:49
【问题描述】:

我是 C++ 新手,目前正在自学一本书。这本书似乎说有几种数组取决于你如何声明它。我想动态数组和静态数组之间的区别对我来说很清楚。但是我不明白 STL std::array 类和静态数组之间的区别。

一个 STL std::array 变量声明为:

std::array < int, arraySize > array1;

而静态数组变量声明为:

int array1[arraySize];

两者之间有根本区别吗?还是只是语法,两者基本相同?

【问题讨论】:

  • 没有区别,只是语法问题,我相信这仅适用于 C++ 11 及更高版本 但是请注意,STL 声明为您提供了一个可以在数组上使用的函数和迭代器的大工具箱,如另一个你通常必须自己制作内置函数来做琐碎的事情。:检查:en.cppreference.com/w/cpp/container/array
  • 当您开始将std::array&lt;&gt; 传递给函数而不是内置数组时,会有很大的不同。 std::array 知道自己的大小,而内置数组不知道,因为它会衰减为指针。
  • @PaulMcKenzie 您实际上可以通过引用传递 C 样式的数组,在这种情况下,大小将是已知的。
  • 离题小唠叨:就叫它std::array。 STL 几十年前就被纳入标准,array 出现得更晚。

标签: c++ arrays c++11 stl


【解决方案1】:

std::array&lt;&gt; 只是一个 C 样式数组的轻量级包装器,还有一些额外的漂亮接口成员函数(如 beginend 等)和 typedefs,大致定义为

template<typename T, size_t N>
class array
{
public:
    T _arr[N];
    T& operator[](size_t);
    const T& operator[](size_t) const;
    // other member functions and typedefs
}

一个根本的区别是前者可以通过值传递,而对于后者,你只能传递一个指向它的第一个元素的指针,或者你可以通过引用传递它,但是你不能将它复制到函数中(除了通过std::copy 或手动)。

一个常见的错误是假设每次将 C 样式数组传递给函数时,由于数组衰减为指针,您会丢失其大小。这并非总是如此。如果你通过引用传递它,你可以恢复它的大小,因为在这种情况下没有衰减:

#include <iostream>

template<typename T, size_t N>
void f(T (&arr)[N]) // the type of arr is T(&)[N], not T*
{
    std::cout << "I'm an array of size " << N;
}

int main()
{
    int arr[10];
    f(arr); // outputs its size, there is no decay happening
}

Live on Coliru

【讨论】:

  • 您可以通过引用或指向数组本身的指针来传递 C 样式数组。
  • @SergeyA 我想我有点不清楚,已编辑。问题是你绝对不能按值传递它。
【解决方案2】:

这两者之间的主要区别是一个重要的区别。

除了 STL 为您提供的好方法之外,当将 std::array 传递给函数时,没有衰减。这意味着,当您在函数中收到std::array 时,它仍然是std::array,但是当您将int[] 数组传递给函数时,它实际上会衰减为int* 指针,并且数组的大小为丢了。

这种差异是一个主要差异。一旦丢失了数组大小,代码现在很容易出现很多错误,因为您必须手动跟踪数组大小。 sizeof() 返回指针类型的大小,而不是数组中元素的数量。这迫使您使用process(int *array, int size) 等接口手动跟踪数组大小。这是一个不错的解决方案,但容易出错。

请参阅 Bjarne Stroustroup 的指南:

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rp-run-time

可以使用更好的数据类型来避免这种情况,std::array 是为它设计的,以及许多其他 STL 类。

附带说明,除非有充分的理由使用固定大小的数组,否则std::vector 作为连续内存数据结构可能是更好的选择。

【讨论】:

    【解决方案3】:

    std::array 和 C 风格的数组类似:

    • 它们都存储连续的对象序列
    • 它们都是聚合类型,因此可以使用aggregate initialization 进行初始化
    • 它们的大小在编译时是已知的
    • 它们不使用动态内存分配

    std::array 的一个重要优点是它可以按值传递,并且不会像 C 样式数组那样隐式衰减到指针。

    【讨论】:

      【解决方案4】:

      在这两种情况下,数组都是在堆栈上创建的。

      但是,STL 的 std::array 类模板比第二种情况的“原始”类 C 数组语法提供了一些优势:

      int array1[arraySize];
      

      例如,使用std::array,您有一个典型的 STL 接口,具有诸如size(可用于查询数组的元素计数)、frontbackat 等方法。

      您可以找到更多详细信息here

      【讨论】:

      • "在这两种情况下,数组都是在堆栈上创建的" - 除非它是在堆上分配的结构/类的成员。
      【解决方案5】:

      两者之间有根本区别吗?还是只是语法,两者基本相同?

      原始 c 样式数组(内置数组)与 std::array 存在许多差异。

      正如您从参考文档中看到的,有许多可用的操作不是原始数组:

      例如:元素访问

      at()
      front()
      back()
      data()
      

      std::array 的底层数据类型仍然是一个原始数组,但带有 “语法糖”(如果您应该关心的话)。

      【讨论】:

        【解决方案6】:

        std::array&lt;&gt; 和 C 样式数组的主要区别在于前者是一个包裹后者的类。该类具有begin()end() 方法,允许std::array 对象作为参数轻松传递给期望迭代器的STL 算法(请注意,C 样式数组也可以通过非成员std::begin/std::end 方法)。第一个指向数组的开头,第二个指向超出数组末尾的一个元素。您会在其他 STL 容器中看到这种模式,例如 std::vectorstd::mapstd::set 等。

        STL std::array 的另一个优点是它有一个 size() 方法,可以让您获取元素计数。要获取 C 样式数组的元素计数,您必须编写 sizeof(cArray)/sizeof(cArray[0]),所以 stlArray.size() 看起来不是更具可读性吗?

        你可以在这里得到完整的参考:

        http://en.cppreference.com/w/cpp/container/array

        【讨论】:

        • 非成员 beginend 在 C 样式数组中工作得很好。有了这些其他改进(转向非成员模板),您所说的一切都是正确的:数组可以由需要包装器的算法处理。数组总是有迭代器;让它们像指针一样是基本原则。
        • 添加了关于非会员开始/结束的注释。
        • «要获取 C 样式数组的元素计数,您必须编写 » 哎呀,不,您不需要。 vsoftco 的答案中显示的模板说明了如何将 N 推导出为模板参数。或者只使用end(a)-begin(a),但是当您应该传递迭代器或“范围”时,为什么需要这样做。 C 数组 一个非常好的范围:由于任何这些原因,都不需要数组的包装器。
        【解决方案7】:

        通常你应该更喜欢std::array&lt;T, size&gt; array1; 而不是T array2[size];,尽管底层结构是相同的。

        主要原因是std::array 总是知道它的大小。您可以调用它的size() 方法来获取大小。而当您使用 C 风格的数组(即所谓的“内置数组”)时,您总是必须将大小传递给使用该数组的函数。如果你弄错了,你可能会导致缓冲区溢出,并且函数会尝试读取/写入不再属于数组的内存。 std::array 不会发生这种情况,因为大小始终是明确的。

        【讨论】:

        • 在 C++ 中,不需要总是传递大小,即使对于 C 样式的数组也是如此。您可以通过模板函数通过引用传递数组并恢复其大小。
        【解决方案8】:

        国际海事组织,

        • 优点:它很高效,因为它不使用比内置固定数组更多的内存。

        • 缺点:std::array 在内置固定数组上是一种稍微笨拙的语法,并且您必须显式指定数组长度(编译器不会从初始化程序中为您计算它)。

        【讨论】:

          猜你喜欢
          • 2016-12-09
          • 2015-03-01
          • 1970-01-01
          • 2020-10-24
          • 2020-02-15
          • 2012-07-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多