Обьясните часть кода плс:
for (auto ptr{ &data[0] }; ptr != (&data[0] + length); ++ptr)
{
std::cout << *ptr << ' ';
}
C/C++
Помогите по c++.
1. Имя массива – указатель на его первый элемент.
2. auto автоматически выводит тип данных.
Таким образом обращаться к элементам массива можно не только по индексам но и по указателям.
Указатель на первый элемент массива можно получить несколькими способами. Например, для массива data, размером length, взяв адрес значения первого элемента в массиве:
auto ptr = &data[0]; // простая инициализация копированием
auto ptr{ &data[0] }; // прямая агрегатная инициализация
auto ptr(&data[0]); // прямая инициализация вызовом конструктора
Работая с адресами элементов в массиве важно не выйти за его пределы. С этой целью следует определить адрес, который находится за последним элементом массива. Для этого достаточно к адресу первого элемента массива прибавить размер массива.
auto ptr = &data[0] + length;
или ещё проще, обратившись через индекс к этому адресу в памяти
auto end = &data[length];
auto end{ &data[length] };
auto end(&data[length]);
В этих случаях циклы принимают следующий вид:
for (auto ptr = &data[0]; ptr != &data[length]; ++ptr) std::cout << *ptr << ' ';
for (auto ptr{ &data[0] }; ptr != &data[length]; ++ptr) std::cout << *ptr << ' ';
for (auto ptr(&data[0]); ptr != end; ++ptr) std::cout << *ptr << ' ';
Существуют и менее запутанный синтаксис работы с адресами массива:
auto ptr = data;
auto end = data + length;
for (auto ptr = data; ptr != data + length; ++ptr) std::cout << *ptr << ' ';
for (auto ptr = data; ptr != end ; ++ptr) std::cout << *ptr << ' ';
Кроме того к статическому массиву, находясь в области его видимости, можно обратиться с использованием стандартных глобальных функций begin и end
auto ptr_b = begin(data);
auto ptr_e = end(data);
for (auto ptr = begin(data); ptr != end(data) ; ++ptr) std::cout << *ptr << ' ';
Однако, если нужно пройтись во всем элементам массива, то в таком случае следует вообще избегать адресной арифметики и использовать возможности range-based for loop
for (auto value : data) std::cout << value << ' ';
Если же вам нужно изменять значения в одномерном статическом массиве, то также, находясь в его области видимости, следует выводить его ссылочный тип:
for (auto& value : data) value *= 2;
P.S. Пример, который вы указали в вопросе, является наихудшим вариантом решения, учитывая, что используемая в примере прямая агрегатная инициализация, утверждённая в стандарте C++11 и повсеместно реализованная компиляторами в стандарте C++14, говорит о возможности использования range-based for loop, который реализован компиляторами стандарта C++11.
2. auto автоматически выводит тип данных.
Таким образом обращаться к элементам массива можно не только по индексам но и по указателям.
Указатель на первый элемент массива можно получить несколькими способами. Например, для массива data, размером length, взяв адрес значения первого элемента в массиве:
auto ptr = &data[0]; // простая инициализация копированием
auto ptr{ &data[0] }; // прямая агрегатная инициализация
auto ptr(&data[0]); // прямая инициализация вызовом конструктора
Работая с адресами элементов в массиве важно не выйти за его пределы. С этой целью следует определить адрес, который находится за последним элементом массива. Для этого достаточно к адресу первого элемента массива прибавить размер массива.
auto ptr = &data[0] + length;
или ещё проще, обратившись через индекс к этому адресу в памяти
auto end = &data[length];
auto end{ &data[length] };
auto end(&data[length]);
В этих случаях циклы принимают следующий вид:
for (auto ptr = &data[0]; ptr != &data[length]; ++ptr) std::cout << *ptr << ' ';
for (auto ptr{ &data[0] }; ptr != &data[length]; ++ptr) std::cout << *ptr << ' ';
for (auto ptr(&data[0]); ptr != end; ++ptr) std::cout << *ptr << ' ';
Существуют и менее запутанный синтаксис работы с адресами массива:
auto ptr = data;
auto end = data + length;
for (auto ptr = data; ptr != data + length; ++ptr) std::cout << *ptr << ' ';
for (auto ptr = data; ptr != end ; ++ptr) std::cout << *ptr << ' ';
Кроме того к статическому массиву, находясь в области его видимости, можно обратиться с использованием стандартных глобальных функций begin и end
auto ptr_b = begin(data);
auto ptr_e = end(data);
for (auto ptr = begin(data); ptr != end(data) ; ++ptr) std::cout << *ptr << ' ';
Однако, если нужно пройтись во всем элементам массива, то в таком случае следует вообще избегать адресной арифметики и использовать возможности range-based for loop
for (auto value : data) std::cout << value << ' ';
Если же вам нужно изменять значения в одномерном статическом массиве, то также, находясь в его области видимости, следует выводить его ссылочный тип:
for (auto& value : data) value *= 2;
P.S. Пример, который вы указали в вопросе, является наихудшим вариантом решения, учитывая, что используемая в примере прямая агрегатная инициализация, утверждённая в стандарте C++11 и повсеместно реализованная компиляторами в стандарте C++14, говорит о возможности использования range-based for loop, который реализован компиляторами стандарта C++11.
Юрий Руссу
А как-же algorithm + copy с итератором для вывода? =) Не подкалываю - улыбаюсь =)
Юрий Руссу
Я-ж видел, что использовали =)
Женя Кораблёв
спасибо, очень понятно
вывести первые length элементов массива data
std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope.
The object is disposed of, using the associated deleter when either of the following happens:
the managing unique_ptr object is destroyed
the managing unique_ptr object is assigned another pointer via operator= or reset().
The object is disposed of, using a potentially user-supplied deleter by calling get_deleter()(ptr). The default deleter uses the delete operator, which destroys the object and deallocates the memory.
A unique_ptr may alternatively own no object, in which case it is called empty.
There are two versions of std::unique_ptr:
1) Manages a single object (e.g. allocated with new)
2) Manages a dynamically-allocated array of objects (e.g. allocated with new[])
The class satisfies the requirements of MoveConstructible and MoveAssignable, but of neither CopyConstructible nor CopyAssignable.
Type requirements
-Deleter must be FunctionObject or lvalue reference to a FunctionObject or lvalue reference to function, callable with an argument of type unique_ptr::pointer
Notes
Only non-const unique_ptr can transfer the ownership of the managed object to another unique_ptr. If an object's lifetime is managed by a const std::unique_ptr, it is limited to the scope in which the pointer was created.
std::unique_ptr is commonly used to manage the lifetime of objects, including:
providing exception safety to classes and functions that handle objects with dynamic lifetime, by guaranteeing deletion on both normal exit and exit through exception
passing ownership of uniquely-owned objects with dynamic lifetime into functions
acquiring ownership of uniquely-owned objects with dynamic lifetime from functions
as the element type in move-aware containers, such as std::vector, which hold pointers to dynamically-allocated objects (e.g. if polymorphic behavior is desired)
std::unique_ptr may be constructed for an incomplete type T, such as to facilitate the use as a handle in the pImpl idiom. If the default deleter is used, T must be complete at the point in code where the deleter is invoked, which happens in the destructor, move assignment operator, and reset member function of std::unique_ptr. (Conversely, std::shared_ptr can't be constructed from a raw pointer to incomplete type, but can be destroyed where T is incomplete). Note that if T is a class template specialization, use of unique_ptr as an operand, e.g. !p requires T's parameters to be complete due to ADL.
If T is a derived class of some base B, then std::unique_ptr is implicitly convertible to std::unique_ptr. The default deleter of the resulting std::unique_ptr will use operator delete for B, leading to undefined behavior unless the destructor of B is virtual. Note that std::shared_ptr behaves differently: std::shared_ptr will use the operator delete for the type T and the owned object will be deleted correctly even if the destructor of B is not virtual.
Unlike std::shared_ptr, std::unique_ptr may manage an object through any custom handle type that satisfies NullablePointer. This allows, for example, managing objects located in shared memory, by supplying a Deleter that defines typedef boost::offset_ptr pointer;
The object is disposed of, using the associated deleter when either of the following happens:
the managing unique_ptr object is destroyed
the managing unique_ptr object is assigned another pointer via operator= or reset().
The object is disposed of, using a potentially user-supplied deleter by calling get_deleter()(ptr). The default deleter uses the delete operator, which destroys the object and deallocates the memory.
A unique_ptr may alternatively own no object, in which case it is called empty.
There are two versions of std::unique_ptr:
1) Manages a single object (e.g. allocated with new)
2) Manages a dynamically-allocated array of objects (e.g. allocated with new[])
The class satisfies the requirements of MoveConstructible and MoveAssignable, but of neither CopyConstructible nor CopyAssignable.
Type requirements
-Deleter must be FunctionObject or lvalue reference to a FunctionObject or lvalue reference to function, callable with an argument of type unique_ptr::pointer
Notes
Only non-const unique_ptr can transfer the ownership of the managed object to another unique_ptr. If an object's lifetime is managed by a const std::unique_ptr, it is limited to the scope in which the pointer was created.
std::unique_ptr is commonly used to manage the lifetime of objects, including:
providing exception safety to classes and functions that handle objects with dynamic lifetime, by guaranteeing deletion on both normal exit and exit through exception
passing ownership of uniquely-owned objects with dynamic lifetime into functions
acquiring ownership of uniquely-owned objects with dynamic lifetime from functions
as the element type in move-aware containers, such as std::vector, which hold pointers to dynamically-allocated objects (e.g. if polymorphic behavior is desired)
std::unique_ptr may be constructed for an incomplete type T, such as to facilitate the use as a handle in the pImpl idiom. If the default deleter is used, T must be complete at the point in code where the deleter is invoked, which happens in the destructor, move assignment operator, and reset member function of std::unique_ptr. (Conversely, std::shared_ptr can't be constructed from a raw pointer to incomplete type, but can be destroyed where T is incomplete). Note that if T is a class template specialization, use of unique_ptr as an operand, e.g. !p requires T's parameters to be complete due to ADL.
If T is a derived class of some base B, then std::unique_ptr is implicitly convertible to std::unique_ptr. The default deleter of the resulting std::unique_ptr will use operator delete for B, leading to undefined behavior unless the destructor of B is virtual. Note that std::shared_ptr behaves differently: std::shared_ptr will use the operator delete for the type T and the owned object will be deleted correctly even if the destructor of B is not virtual.
Unlike std::shared_ptr, std::unique_ptr may manage an object through any custom handle type that satisfies NullablePointer. This allows, for example, managing objects located in shared memory, by supplying a Deleter that defines typedef boost::offset_ptr pointer;