【问题标题】:Passing object array with std::array vs array使用 std::array 与数组传递对象数组
【发布时间】:2018-02-26 17:17:52
【问题描述】:

今天我正在和我的朋友讨论在 C++ 中传递对象数组的正确方法。这两者之间是否有效率差异:

Struct Apple {
    std::string color;
}

void colors(Apple A[]) {
    A[0].color = "red";
}

int main() {
    Appple apples[10];
    colors(apples);
    return 0;
}

.

Struct Apple {
    std::string color;
}

void colors(std::array<Apple, 10>& A) {
    A[0].color = "red";
}

int main() {
    std::array<Apple, 10> apples;
    colors(apples);
    return 0;
}

【问题讨论】:

  • 性能方面?
  • 是的。对不起,如果我不清楚:)。
  • @Marker 它发生在编译时
  • '代码 A 比代码 B 快吗? - 为什么不尝试一下并找出答案。
  • @Marker std::array 是一个聚合,因此它没有构造函数。如果你把一些可以简单初始化的东西放在里面,那么只会有一个堆栈分配,仅此而已。这是一个零成本的抽象。

标签: c++ arrays object struct stdarray


【解决方案1】:

这两个函数在x86_64 上使用GCC 7.3 生成相同的代码。

这里:https://godbolt.org/g/aguVfF

第一个例子:

void colors(Apple A[]) {
    A[0].color = 12;
}

_Z6colorsP5Apple: 
  mov DWORD PTR [rdi], 12
  ret

第二个例子:

void colors(std::array<Apple, 10>& A) {
    A[0].color = 13;
}

_Z6colorsRSt5arrayI5AppleLm10EE:
  mov DWORD PTR [rdi], 13
  ret

当传递内置数组时,它们会衰减为指向其第一个元素的指针。此外,当对象通过引用传递时,通常使用指针实现。

因为std::array 是一个非常薄 对内置数组的包装器,所以它的地址很可能与其第一个元素的地址相同。编译器的优化器可以看穿它。

您不太可能找到任何使用 std::array 比内置数组慢的示例,因为 std::array 基本上是一个包含在零成本中的内置数组语法糖,它利用了内置版本没有的编译时间信息(如数组的大小)。

【讨论】:

    【解决方案2】:

    您在使用 C 和 C++ 时都会遇到一个常见的初学者错误——您不能将数组作为参数传递给函数。语言不允许。如果你声明一个以数组作为参数的函数,编译器(默默地)将它更改为你的指针,因为数组可以隐式转换为指针(通常在大多数地方都是如此)。

    所以你的第一个例子真的是:

    void colors(Apple *A) {
        A[0].color = "red";
    }
    

    所以现在很明显你的问题是:传递指针和传递引用在性能上有什么区别吗?

    不——引用和指针以相同的方式实现——作为地址——所以它们之间的性能没有差异。

    【讨论】:

    • 所以void(int A[])void(int *A) 不一样?
    • @dqmis:它们都不是函数——它们是类型。
    • 我投了反对票,因为说数组作为指向其第一个参数的指针传递并不能真正证明 std::array 更快或更慢,或者原始数组与 std::array 有其他差异(安全、语义、规模、防泄漏、可读性……)
    • @jotik:“撤销”其他人的投票权不是你的工作。
    • 我投了赞成票,因为即使它没有直接回答 OP 问题(实际上我认为确实如此),它也有一个互补的价值,有助于更好地理解问题。
    【解决方案3】:

    在性能方面,没有。它们都只是在内存地址附近移动,因此性能是相同的。

    在风格方面,我总是推荐 std::array 而不是 C 数组。 C 数组仅在 C++ 中用于与旧代码向后兼容。新东西具有遵循标准容器类接口的好处,因此可以与&lt;algorithm&gt; 和其他库中的所有花哨的 STL 泛型方法一起使用。它也知道自己的大小,这本身就是一个很大的胜利。

    【讨论】:

      【解决方案4】:

      性能方面,它取决于数组的大小、编译器和使用的编译选项。基准测试。

      当你传递一个常规数组时,它基本上会衰减为一个指针,所以你只有传递一个指针的开销。如果您通过复制传递std::array,那么您需要复制整个数组。如果你通过引用传递它,那么你就会有传递引用的开销(这通常与传递指针相同)。

      我仍然强烈建议您使用 std::array 来保证类型安全。

      【讨论】:

      • 也许不是更安全的类型,但它首先知道自己的大小。
      • 作为一个额外的好处,如果您通过引用传递,编译器可以更确定变量在内存中的位置,因此这允许在大型程序中进行更多优化。当您在编译器优化中处理指针时,存在大量不确定性。
      • @jotik 都是不错的功能,但类型安全性不同...
      • 请注意,对于std::array 示例,函数的参数是引用,因此它不是复制。
      • 通过引用传递的本机数组也带有它的大小。 void colors(Apple (&amp;)[10]);
      猜你喜欢
      • 2015-07-27
      • 1970-01-01
      • 2019-01-17
      • 1970-01-01
      • 2014-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多