基于范围的堆分配数组的循环

时间:2018-08-27 11:22:19

标签: c++ c++11 pointers

请考虑以下代码,它们将准确success引用三遍:

int arr [3];

for(int& value : arr )
    std::cout << "success" << std::endl;

如果我尝试在堆上分配数组,则会出现问题。此代码无法编译:

int* ptr = new int[3];

for(int& value : *ptr )
    std::cout << "success" << std::endl;

由于指针已被取消引用,因此类型应相同。所以我有一些问题:

  • 当我从两个表达式中的硬件询问时,根本的区别是什么。我想了解为什么后者没有意义。
  • 我可以做一点零钱吗?

2 个答案:

答案 0 :(得分:4)

如果可见声明包含元素数,即原始情况下的int arr[3]和第二情况下的int* prt,则原始数组仅支持基于范围的语法。在第一种情况下,给出了此信息(但是,如果可能,您仍应首选std::array),但是在第二种情况下,则不会在堆上分配内存,并且有关大小的信息已消失。如果您仅使用std::array而不是原始数组,则可以避免这种情况。

  

由于指针已被取消引用,因此类型应相同。

仔细研究一下,原因是,在第一种情况下,您有一个数组,在第二种情况下,您有一个指针,即使相同的类型也

这种对指针和数组相等性的误解一直在C ++教程中传播,但这是错误的。这可能是由于以下事实:将数组传递给带有该类型指针的函数时,数组 decays 指向一个指针,即 array-decay

  

我可以做一点改动吗

可以。使用std::arrayst::vector会使std::array看起来像这样:

#include <iostream>
#include <array>

int main()
{
    std::array<int, 3>* ptr = new std::array<int, 3>;

    for(int& value : *ptr )
        std::cout << "success" << std::endl;
}

为简便起见,我没有包括您应该经常删除的指针。

但是,如果您在堆上分配内存,则几乎总是最好使用std::vector,因为自动分配会自动处理。该程序将显示为:

#include <iostream>
#include <vecor>

int main()
{
    std::vector<int> vec(3);

    for(int& value : vec)
        std::cout << "success" << std::endl;
}

答案 1 :(得分:4)

  

由于指针已被取消引用,因此类型应相同。

仔细查看指针的类型:int* ptr。它是指向int的指针,将其与arr的类型int[3]进行比较。这些类型是不同的。因此,您认为类型应该相同是错误的。 int[3]是范围,而int不是范围。

array-new-expression返回一个指向数组第一个元素的指针。这就是ptr所指向的。第一个元素的内存地址与整个数组相同,但是变量的类型决定了如何使用它。

  

我可以做一点零钱吗?

由于指向第一个元素的指针的值与指向整个数组的指针的值相同,因此可以使用强制转换将指针重新解释为另一种类型:

auto arr_ptr = std::launder(reinterpret_cast<int (*)[3]>(ptr));
for(int& value : *arr_ptr)

也就是说,通常使用动态数组,因为它们允许在运行时确定大小。该强制类型转换的大小必须在编译时就知道。

此外,您的示例代码泄漏了数组。尽管此代码中的泄漏很容易修复,但是手动进行内存管理通常很困难,而且没有必要。当然,当您需要动态数组时,最好使用std::vector

std::vector<int> v(3);
for(int& value : v)