數(shù)組和C++ std::array詳解
1. 數(shù)組和std::array
std::array
是C++容器庫提供的一個固定大小數(shù)組的容器。其與內(nèi)置的數(shù)組相比,是一種更安全、更容易使用的數(shù)組類型。std::array
在頭文件<array>中定義,其聲明如下:
template<
? ?class T,
? ?std::size_t N
> struct array; //C++11 起
T[N]
作為其唯一非靜態(tài)數(shù)據(jù)成員的結(jié)構(gòu)體,但其不同于C數(shù)組的是它不會自動退化為T*
。同時該結(jié)構(gòu)體結(jié)合了C風(fēng)格數(shù)組的性能、可訪問性和容器的優(yōu)點(可獲取大小、支持賦值和隨機訪問等)。
2. array的用法
2.1 成員函數(shù)
2.1.1 隱式定義的成員函數(shù)
構(gòu)造函數(shù)(隱式聲明)遵循聚合初始化的規(guī)則初始化 array(注意默認(rèn)初始化可以導(dǎo)致非類的T的不確定值)析構(gòu)函數(shù)(隱式聲明)銷毀 array 的每個元素operator=(隱式聲明)以來自另一 array的每個元素重寫array的對應(yīng)元素
聚合初始化就是從初始化器列表來初始化聚合體,其也是列表初始化的一種方式。
std::array<int, 3> a = {1,2,3};
std::array<int, 3> b;
b = a; ?//將a中的每個元素重寫到b中,使用operator=時候需要確保a b兩個容器長度相等,否則編譯失敗
2.1.2 元素訪問
at
at用于訪問指定的元素,同時進行越界檢查,該函數(shù)返回位于指定位置pos的元素的引用,如果pos不在容器的范圍內(nèi),則拋出std::out_of_range
異常。其函數(shù)聲明如下:
reference at( size_type pos ); //C++17 前
constexpr reference at( size_type pos ); //C++17 起
const_reference at( size_type pos ) const; //C++14 前
constexpr const_reference at( size_type pos ) const; //C++14 起
其具體用法如下:
std::array<int,3> data = { 1, 2, 3};
std::cout<<data.at(1)<<std::endl; //2
data.at(1)=8; //此時data={1, 8, 3}
data.at(6) = 6; //越界,拋出std::out_of_range異常
operator[]
operator[]與at功能相同,即用來訪問指定的元素,但其與at不同的是:operator[]不進行邊界的檢查。其函數(shù)聲明如下所示:
reference operator[]( size_type pos ); //C++17 前
constexpr reference operator[]( size_type pos ); //C++17 起
const_reference operator[]( size_type pos ) const; //C++14 前
constexpr const_reference operator[]( size_type pos ) const; //C++14 起
注:通過operator[]符訪問不存在的元素是未定義行為。
front
front用于訪問容器的第一個元素,其返回值為容器首元素的引用,其函數(shù)原型如下:
reference front(); //C++17 前
constexpr reference front(); //C++17 起
const_reference front() const; //C++14 前
constexpr const_reference front() const; //C++14 起
注:在空容器上對
front
的調(diào)用是未定義的。
back
back主要功能是用來訪問容器最后一個元素,其返回值為容器最后一個元素的引用,其函數(shù)原型如下所示:
reference back(); //C++17 前
constexpr reference back(); //C++17 起
const_reference back() const; //C++14 前
constexpr const_reference back() const; //C++14 起
注:在空容器上調(diào)用
back
導(dǎo)致未定義行為。
data
data可以直接訪問容器底層數(shù)組,其返回值為指向作為元素存儲工作的底層數(shù)組的指針。其函數(shù)聲明如下:
T* data() noexcept; //C++11 起, C++17 前
constexpr T* data() noexcept; //C++17 起
const T* data() const noexcept; //C++11 起, C++17 前
constexpr const T* data() const noexcept; //C++17 起
其返回的指針使得范圍[ data()
, data() + size()
)始終是合法范圍。
2.2.3 迭代器
begin、end和cbegin、cend
begin和cbegin返回指向deque首元素的迭代器,end和cend返回指向deque末元素后一元素的迭代器。其函數(shù)聲明如下:
iterator begin() noexcept; //C++17 前
constexpr iterator begin() noexcept; //C++17 起
const_iterator begin() const noexcept; //C++17 前
constexpr const_iterator begin() const noexcept; //C++17 起
const_iterator cbegin() const noexcept; //C++17 前
constexpr const_iterator cbegin() const noexcept; //C++17 起
iterator end() noexcept; //C++17 前
constexpr iterator end() noexcept; //C++17 起
const_iterator end() const noexcept; //C++17 前
constexpr const_iterator end() const noexcept; //C++17 起
const_iterator cend() const noexcept; //C++17 前
constexpr const_iterator cend() const noexcept; //C++17 起
如果array為空,則返回的迭代器將等于end或cend。end和cend指向deque末元素后一元素的迭代器,該元素的表現(xiàn)為占位符,試圖訪問它將導(dǎo)致未定義行為。
rbegin、rend和crbegin、crend
rbegin和crbegin返回指向array首元素的逆向迭代器。它對應(yīng)非逆向array的末元素,若array為空,則返回的迭代器等于rend或crend。rend和crend返回指向逆向deque末元素后一元素的逆向迭代器,它對應(yīng)非逆向array首元素的前一元素,此元素表現(xiàn)為占位符,試圖訪問它導(dǎo)致未定義行為。它們的聲明如下:
reverse_iterator rbegin() noexcept; //C++17 前
constexpr reverse_iterator rbegin() noexcept; //C++17 起
const_reverse_iterator rbegin() const noexcept; //C++17 前
constexpr const_reverse_iterator ?rbegin() const noexcept; //C++17 起
const_reverse_iterator crbegin() const noexcept; //C++17 前
constexpr const_reverse_iterator crbegin() const noexcept; //C++17 起
reverse_iterator rend(); //C++11 前
reverse_iterator rend() noexcept; //C++11 起
const_reverse_iterator rend() const; //C++11 前
const_reverse_iterator rend() const noexcept; //C++11 起
const_reverse_iterator crend() const noexcept; //C++11 起
2.2.4 容量
empty
empty用來檢查容器是否為空,若為空則返回true,否則為false。其函數(shù)聲明如下:
constexpr bool empty() const noexcept; //C++11 起,C++20 前
[[nodiscard]] constexpr bool empty() const noexcept; //C++20 起
其底層實現(xiàn)就是檢查容器是否無元素,即判斷是否begin() == end()
。
size
size函數(shù)返回容器中元素數(shù)量,即std::distance(begin(), end())
。其函數(shù)聲明如下:
constexpr size_type size() const noexcept; //C++11 起
max_size
max_size函數(shù)返回根據(jù)系統(tǒng)或庫實現(xiàn)限制的容器可保有的元素最大數(shù)量,即對于最大容器的 std::distance(begin(), end())
。其函數(shù)聲明為:
constexpr size_type max_size() const noexcept; //C++11 起
注:因為每個
std::array<T, N>
都是固定大小容器,故max_size
返回的值等于N
(亦為size所返回的值)
2.2.5 修改器
fill
fill函數(shù)原型如下所示:
void fill( const T& value ); //C++11 起, C++20 前
constexpr void fill( const T& value ); //C++20 起
fill函數(shù)主要用于以指定值填充容器,即將定值 value
賦給容器中的所有元素。
具體用法示例如下:
std::array<int, 3> arr = {1, 2, 3};
arr.fill(1); // arr = {1, 1, 1}
swap
swap函數(shù)的主要作用是交換兩個array容器的內(nèi)容,其與deque的
不同的是不導(dǎo)致迭代器和引用關(guān)聯(lián)到別的容器。其函數(shù)聲明如下:void swap( array& other ) noexcept(); //C++11 起, C++20 前
constexpr void swap( array& other ) noexcept(); //C++20 起
其用法示例如下圖所示:
std::array<int, 3> a1{1, 2, 3}, a2{4, 5, 6};
auto it1 = a1.begin(); //*it1 = 1
auto it2 = a2.begin(); //*it2 = 4
int &ref1 = a1[1]; // ref1 = 2
int &ref2 = a2[1]; // ref1 = 5
std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印結(jié)果為1 4 2 5
a1.swap(a2);
// 此時a1 = {4, 5, 6},a2 = {1, 2, 3}
std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印結(jié)果為4 1 5 2
/*注:
? ? 交換后迭代器與引用保持與原 array 關(guān)聯(lián),
? ? 例如it1仍指向元素 a1[0] ,ref1仍指代 a1[1] */
2.2 非成員函數(shù)
operator==,!=,<,<=,>,>=,<=>(std::array)
C++提供operator==,!=,<,<=,>,>=,<=>(std::array)
非成員函數(shù)用來比較兩個array的大小,相關(guān)函數(shù)及函數(shù)聲明如下:
//1. ==
//返回值:在 array 內(nèi)容相等時返回 true,否則返回 false
template< class T, std::size_t N >
bool operator==( const std::array<T, N>& lhs,
? ? ? ? ? ? ? ? const std::array<T, N>& rhs ); //C++20 前
template< class T, std::size_t N >
constexpr bool operator==( const std::array<T, N>& lhs,
? ? ? ? ? ? ? ? ? ? ? ? ? const std::array<T, N>& rhs ); //C++20 起
//2. !=
//返回值:在 array 內(nèi)容不相等時返回 true,否則返回 false
template< class T, std::size_t N >
bool operator!=( const std::array<T, N>& lhs,
? ? ? ? ? ? ? ? const std::array<T, N>& rhs ); //C++20 前
//3. <
//返回值:在 lhs 的內(nèi)容按字典序小于 rhs 的內(nèi)容時返回 true,否則返回 false
template< class T, std::size_t N >
bool operator<( const std::array<T, N>& lhs,
? ? ? ? ? ? ? ?const std::array<T, N>& rhs ); //C++20 前
//4. <=
//返回值:在 lhs 的內(nèi)容按字典序小于或等于 rhs 的內(nèi)容時返回 true,否則返回 false
template< class T, std::size_t N >
bool operator<=( const std::array<T, N>& lhs,
? ? ? ? ? ? ? ? const std::array<T, N>& rhs ); //C++20 前
//5. >
//返回值:在 lhs 的內(nèi)容按字典序大于 rhs 的內(nèi)容時返回 true,否則返回 false
template< class T, std::size_t N >
bool operator>( const std::array<T, N>& lhs,
? ? ? ? ? ? ? ?const std::array<T, N>& rhs ); //C++20 前
//6. >=
//返回值:在 lhs 的內(nèi)容按字典序大于或等于 rhs 的內(nèi)容時返回 true,否則返回 false
template< class T, std::size_t N >
bool operator>=( const std::array<T, N>& lhs,
? ? ? ? ? ? ? ? const std::array<T, N>& rhs ); //C++20 前
//7. <=>
//返回值:lhs 與 rhs 中的首對不等價元素的相對順序,如果有這種元素;否則是 lhs.size() <=> rhs.size()。
template< class T, std::size_t N >
constexpr operator<=>( const std::array<T, N>& lhs,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const std::array<T, N>& rhs ); //C++20 起
1,2中會檢查lhs和rhs的內(nèi)容是相等,即他們是否擁有相同數(shù)量的元素且lhs中每個元素與rhs的相同位置元素比較相等。同時函數(shù)中
T
必須符合可相等比較 (EqualityComparable) 的要求3-6中按照字典比較lhs和rhs的內(nèi)容,其內(nèi)部等價于調(diào)用
std::lexicographical_compare
函數(shù)進行比較。同時函數(shù)中T
必須符合[可小于比較 (LessThanComparable) 的要求。7中也是按字典序比較lhs和rhs的內(nèi)容。其內(nèi)部等價于調(diào)用
lhs < rhs ? std::weak_ordering::less :std::lexicographical_compare_three_way
進行比較。返回類型同合成三路比較的結(jié)果類型。其邏輯大致如下:
rhs < lhs ? std::weak_ordering::greater :
? ? ? ? ? ?std::weak_ordering::equivalent
//注:通常情況下less對應(yīng)的是-1,greater對應(yīng)1,equivalent對應(yīng)0lhs與rhs中的首對不等價元素的相對順序,如果有這種元素;否則是
lhs.size() <=> rhs.size()
。
其具體的應(yīng)用示例如下所示:
std::array<int, 3> alice{1, 2, 3};
std::array<int, 3> bob{7, 8, 9};
std::array<int, 3> eve{1, 2, 3};
std::cout << std::boolalpha;
// 比較不相等的容器
std::cout << "alice == bob returns " << (alice == bob) << '\n';
std::cout << "alice != bob returns " << (alice != bob) << '\n';
std::cout << "alice < ?bob returns " << (alice < bob) << '\n';
std::cout << "alice <= bob returns " << (alice <= bob) << '\n';
std::cout << "alice > ?bob returns " << (alice > bob) << '\n';
std::cout << "alice >= bob returns " << (alice >= bob) << '\n';
std::cout << '\n';
// 比較相等的容器
std::cout << "alice == eve returns " << (alice == eve) << '\n';
std::cout << "alice != eve returns " << (alice != eve) << '\n';
std::cout << "alice < ?eve returns " << (alice < eve) << '\n';
std::cout << "alice <= eve returns " << (alice <= eve) << '\n';
std::cout << "alice > ?eve returns " << (alice > eve) << '\n';
std::cout << "alice >= eve returns " << (alice >= eve) << '\n';
輸出結(jié)果為
alice == bob returns false
alice != bob returns true
alice < ?bob returns true
alice <= bob returns true
alice > ?bob returns false
alice >= bob returns false
alice == eve returns true
alice != eve returns false
alice < ?eve returns false
alice <= eve returns true
alice > ?eve returns false
alice >= eve returns true
std::get(std::array)
std::get(std::array)
可以用來訪問array的一個元素,其函數(shù)聲明如下:
template< std::size_t I, class T, std::size_t N >
T& get( std::array<T,N>& a ) noexcept; //C++11 起, C++14 前
template< std::size_t I, class T, std::size_t N >
constexpr T& get( std::array<T,N>& a ) noexcept; //C++14 起
template< std::size_t I, class T, std::size_t N >
T&& get( std::array<T,N>&& a ) noexcept; //C++11 起, C++14 前
template< std::size_t I, class T, std::size_t N >
constexpr T&& get( std::array<T,N>&& a ) noexcept; //C++14 起
template< std::size_t I, class T, std::size_t N >
const T& get( const std::array<T,N>& a ) noexcept; //C++11 起, C++14 前
template< std::size_t I, class T, std::size_t N >
constexpr const T& get( const std::array<T,N>& a ) noexcept; //C++14 起
template< std::size_t I, class T, std::size_t N >
const T&& get( const std::array<T,N>&& a ) noexcept; //C++11 起, C++14 前
template< std::size_t I, class T, std::size_t N >
constexpr const T&& get( const std::array<T,N>&& a ) noexcept; //C++14 起
其主要作用是從a
中提取第I
個元素.I
必須是范圍 [0, N)
中的整數(shù)值。與at()
或 operator[]
相反,這在編譯時強制。該函數(shù)的返回值為a
中第I
元素的引用。
其具體的用法如下:
std::array<int, 3> arr;
// 設(shè)置值:
std::get<0>(arr) = 1;
std::get<1>(arr) = 2;
std::get<2>(arr) = 3;
// 獲取值:
std::cout << "(" << std::get<0>(arr) << ", " << std::get<1>(arr)
?<< ", " << std::get<2>(arr) << ")\n";
//輸出結(jié)果為 (1, 2, 3)
std::swap(std::array)
std::swap(std::array)
函數(shù)是為std::array
特化std::swap
算法。其函數(shù)聲明如下:
template< class T, std::size_t N >
void swap( std::array<T, N>& lhs,
? ? ? ? ? std::array<T, N>& rhs ); //C++11 起, C++17 前
template< class T, std::size_t N >
void swap( std::array<T, N>& lhs,
? ? ? ? ? std::array<T, N>& rhs ) noexcept(); //C++17 起, C++20 前
template< class T, std::size_t N >
constexpr void swap( std::array<T, N>& lhs,
? ? ? ? ? ? ? ? ? ? std::array<T, N>& rhs ) noexcept(); //C++20 起
交換 lhs
與 rhs
的內(nèi)容。調(diào)用lhs.swap(rhs)
。其具體用法如下:
std::array<int, 3> a1{1, 2, 3}, a2{4, 5, 6};
auto it1 = a1.begin(); //*it1 = 1
auto it2 = a2.begin(); //*it2 = 4
int &ref1 = a1[1]; // ref1 = 2
int &ref2 = a2[1]; // ref1 = 5
std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印結(jié)果為1 4 2 5
std::swap(a1, a2);
// 此時a1 = {4, 5, 6},a2 = {1, 2, 3}
std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印結(jié)果為4 1 5 2
std::to_array
std::to_array
函數(shù)聲明如下:
template<class T, std::size_t N>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N]); //C++20 起
template<class T, std::size_t N>
constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&&a)[N]); //C++20 起
std::to_array
函數(shù)可以從一維內(nèi)建數(shù)組 a
創(chuàng)建 std::array
對象,從 a
的對應(yīng)元素復(fù)制初始化 std::array
的元素。不支持復(fù)制或移動多維內(nèi)建數(shù)組。其具體用法如下:
#include <array>
#include <iostream>
int main()
{
?// 復(fù)制字符串字面量
?auto a1 = std::to_array("foo");
?static_assert(a1.size() == 4);
?// 推導(dǎo)元素類型和長度
?auto a2 = std::to_array({0, 2, 1, 3});
?// 推導(dǎo)長度而元素類型指定
?// 發(fā)生隱式轉(zhuǎn)換
?auto a3 = std::to_array<long>({0, 1, 3});
?auto a4 = std::to_array<std::pair<int, float>>(
? ?{{3, .0f}, {4, .1f}, {4, .1e23f}});
?// 創(chuàng)建不可復(fù)制的 std::array
?auto a5 = std::to_array({std::make_unique<int>(3)});
?// 錯誤:不支持復(fù)制多維數(shù)組
?// char s[2][6] = { "nice", "thing" };
?// auto a6 = std::to_array(s);
}
std::tuple_size
std::tuple_size(std::array)
函數(shù)的聲明如下:
template< class T, std::size_t N >
struct tuple_size< std::array<T, N> > :
? ?std::integral_constant<std::size_t, N> ? //C++11 起
{ };
其提供作為編譯時常量表達(dá)式訪問std::array
中元素數(shù)量的方法。用法示例如下:
#include <iostream>
#include <array>
template<class T>
void test(T t)
{
? ?int a[std::tuple_size<T>::value]; // 能用于編譯時
? ?std::cout << std::tuple_size<T>::value << '\n';
}
int main()
{
? ?std::array<float, 3> arr;
? ?test(arr); //輸出 3
}
std::tuple_element
std::tuple_element<std::array>
函數(shù)主要用來獲得 array
元素的類型,其聲明如下:
template< std::size_t I, class T, std::size_t N >
struct tuple_element<I, std::array<T, N> >; //C++11 起
其使用類 tuple 接口,提供 array 元素類型的編譯時帶下標(biāo)訪問。具體使用方法如下:
// 定義 array 并獲取位于位置 0 的元素類型
std::array<int, 10> data {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
using T = std::tuple_element<0, decltype(data)>::type; // int
3. 總結(jié)
數(shù)組std::array
的優(yōu)劣:
優(yōu)點
無開銷隨機訪問。
快速遍歷;適合線性搜索。
劣勢
如果元素類型具有較高的復(fù)制/分配成本,則可能會變慢(重新排序元素需要復(fù)制/移動它們)。
在使用array容器的時候,其size必須是常量表達(dá)式(即編譯時已知)。
不支持大小更改操作(調(diào)整大小、插入、擦除等)。
