【问题标题】:Range based for loop for heap allocated arrays堆分配数组的基于范围的for循环
【发布时间】:2018-08-27 11:22:18
【问题描述】:

考虑以下代码,它将精确地计算出success 3 次:

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;

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

  • 当我从硬件上问这两种表达方式的根本区别是什么。我想了解为什么后者没有意义。
  • 我可以通过一个小改动让它工作吗?

【问题讨论】:

  • 使用堆分配数组时,数组的大小信息会丢失。
  • Re: "will cout success" -- cout 是名词,不是动词。
  • 如果range_expression是一个容器,它有成员beginend,否则它是一个调用std::begin(c)的内置数组。 en.cppreference.com/w/cpp/language/range-for

标签: c++ c++11 pointers


【解决方案1】:

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

仔细查看指针的类型: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)

【讨论】:

  • 你确定你没有在 UB 领域使用 reinterpret_cast 运行,因为你没有投射到 char*?
  • 我总是不确定重新解释演员表。
  • @Pi 我不能怪你 :) 我在示例代码中添加了洗钱功能以防万一。
  • 如果类型不相似,则 std::launder 将无济于事。结果据我所知还是UB。
  • 它基本上分解为数组和指针是否相似的问题。如果不是,那就是UB。但老实说,我不知道。
【解决方案2】:

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

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

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

这种对指针和数组相等的误解一直在 C++ 教程中传播,但这是错误的。这可能是因为,当将数组传递给采用该类型指针的函数时,数组衰减 到一个指针,称为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;
}

【讨论】:

    猜你喜欢
    • 2016-10-31
    • 1970-01-01
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-06
    • 1970-01-01
    • 2014-01-12
    相关资源
    最近更新 更多