Диапазон для цикла для распределенных массивов кучи
Рассмотрим следующий код, который будет cout 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 ответа
Необработанный массив поддерживает синтаксис, основанный на диапазоне, только если видимое объявление включает в себя количество элементов, т.е. int arr[3]
в первом случае и int* prt
во втором случае. В первом случае это дается (но все же вы должны предпочесть std::array
если возможно), но не во втором случае вы выделяете память в куче и информация о размере исчезает. Вы можете обойти это, если вы просто используете std::array
а не необработанный массив.
Поскольку указатель был разыменован, типы должны быть одинаковыми.
Если присмотреться поближе, причина этого в том, что в первом случае у вас есть массив, а во втором - указатель, который даже НЕ имеет тот же тип.
Это неверное толкование указателя и выравнивания массива продолжает транслироваться в учебниках по C++, но это неправильно. Это может быть связано с тем, что при передаче массива в функцию, принимающую указатель этого типа, массив распадается на указатель, известный как " распад массива".
Могу ли я заставить его работать с небольшим изменением
Да, ты можешь. использование std::array
или же st::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;
}
Поскольку указатель был разыменован, типы должны быть одинаковыми.
Присмотритесь к типу указателя: int* ptr
, Это указатель на int
Сравните это с типом arr
который int[3]
, Эти типы разные. Итак, ваше предположение, что типы должны быть одинаковыми, неверно. int[3]
это диапазон, в то время как int
не является.
Массив-новое-выражение возвращает указатель на первый элемент массива. Что это 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)