1. 程式人生 > >【C++ STL應用與實現】5: 如何使用std::array (since C++11)

【C++ STL應用與實現】5: 如何使用std::array (since C++11)

本系列文章的目錄在這裡:目錄. 通過目錄裡可以對STL總體有個大概瞭解

前言

本文總結了STL中的序列式容器array的用法及注意事項。array的出現代表著C++的程式碼更進一步“現代化”,就像std::string的出現代替了c風格字串並且能和STL配合工作一樣,array的出現則將取代語言內建的陣列以及c風格的陣列字串,它提供了data()介面,使得能夠獲得內部陣列的首地址,它提供了size(), 能夠得其固定的長度,使得C++的陣列也可以像Java等語言那樣知道自己的length;它提供了begin(), end()等介面使得“陣列”也可以和STL血脈相容;它還提供了tuple介面,可以當做tuple來使用;更重要的一點是,array有並不比原生陣列差的效能表現。

array的概念

array是STL中的一個序列式容器,它包裝了一個c風格的陣列,但在外部介面來看,提供了STL容器的常用介面。它的長度是固定的,正如普通的c風格陣列那樣,一旦建立完成,長度即確定,不能擴大也不能縮小。

它的原型就像這樣, 是一個模板類:

namespace std
{
    template <typename T, size_t N>
    class array;
}

第一個模板引數T指明瞭array中存放的資料型別;

第二個非型別模板引數指明瞭array的固定大小。

array的介面

constructors

建構函式
說明
arrary<T, N> c 預設建構函式,N個元素全部使用“預設初始化行為”來構造。
arrary<T, N> c(other) 拷貝建構函式,拷貝所有other的元素到c來構造。
arrary<T, N> c = other 拷貝建構函式,拷貝所有other的元素到c來構造。
arrary<T, N> c(rValue) 移動構造,使用右值rValue裡的元素來初始化c。
arrary<T, N> c = rValue 移動構造,使用右值rValue裡的元素來初始化c。
arrary<T, N> c = initlist 使用初始化列表初始化元素

注意: 由於預設建構函式是對每一個元素使用“預設構造”行為來初始化,這意味著對於基本型別的資料其初始值是未定義的。

array 被要求是一個“aggregate”: 沒有使用者自定義的建構函式、沒有非靜態的private和protected型別的成員、沒有基類、沒有虛擬函式.

因此不支援這樣的構造方法:array<int, 3> a({1, 2, 4});

初始化array最常用的方法是使用賦值運算子和初始化列表:

array<int, 3> a = {1, 2, 3};

array<int, 100> b = {1, 2, 3};  // a[0] ~ a[2] = 1, 2, 3; a[3] ~ a[99] = 0, 0, 0 ... 0;

array<int, 3> c;                // c[0] ~ c[2] 未初始化,是垃圾值.

assignment

形式 說明
c = other 把other的全部元素複製一份給c。
c = rValue rValue全部元素被移動到c
c.fill(val) 用val給每個元素賦值
c.swap(c2) 交換c和c2的所有元素
swap(c, c2) 交換c和c2的所有元素

注意 :array的swap操作通常代價比較高:是一個O(n)的操作。

begin(), end()等迭代器位置及屬性獲取操作

  • begin() (cbegin())

  • end() (cend())

  • rbegin() (crbegin())

  • rend() (crend())

  • empty()

  • size()

  • max_size()

  • [index]

  • at(index)

- front()

  • back();

tuple介面

array<string, 3> a = {"hello", "hwo", "are"};
tuple_size<a>::value;
tuple_element<1, a>::type;  // string
get<1>(a);                  

把array當做c風格的陣列來用

//----------------------- array as c-style array ----------------------
RUN_GTEST(ArrayTest, CStyleArray, @);

// use array<char> as a fix sized c-string.
array<char, 100> str = {0};           // all elements initialized with 0.

char *p = str.data();

strcpy(p, "hello world");
printf("%s\n", p);              // hello world


END_TEST;

上面這個例子讓我想起了std::string, 它有一個c_str()方法,同樣是返回內部的c風格字串,同樣string也是STL的容器。

這讓我想到,array的出現也是像string那樣,是為了取代舊的c-風格字串和內建陣列,並且添加了標準容器的一些介面使得陣列可以和STL其他元件和諧工作。

綜合示例

//----------------------- normal example ----------------------
RUN_GTEST(ArrayTest, NormalExample, @);

array<int, 5> a = { 1, 2, 3 };
psln(a.size());                     // a.size() = 5;
psln(a.max_size());                 // a.max_size() = 5;
EXPECT_FALSE(a.empty());            // empty() is false.

printContainer(a, "array: ");       // array: 1 2 3 0 0

a[a.size() - 1] = 5;                // change last one
printContainer(a, "array: ");       // array: 1 2 3 0 5

a.at(a.size() - 2) = 4;
printContainer(a, "array: ");       // array: 1 2 3 4 5

int sum;
sum = accumulate(a.begin(), a.end(), 0);
psln(sum);                          // sum = 15

try
{
    int i = a.at(5);                // throw.
}
catch ( ... )
{
    pln("exception catched");
}

try
{
    //int i = a[5];                   // won't throw exception.
}
catch ( ... )
{
    pln("exception catched");       
}

// ------------------ copy ------------------
array<int, 5> a2 = a;                   // copy constructor.
printContainer(a2, "a2: ");             // a2: 1 2 3 4 5

array<int, 5> a3(a2);                   //copy ctor.
printContainer(a3, "a3: ");             // a3: 1 2 3 4 5


// ------------------ assign ------------------
array<int, 5> a4;
a4 = a3;                                // assignment operator.
printContainer(a4, "a4: ");             // a4: 1 2 3 4 5


array<int, 4> b = {};
//b = a;                                // error, b is not array<int, 5>!!


// ------------------ fill ------------------
array<int, 5> a5;
a5.fill(5);
printContainer(a5, "a5: ");             // a5: 5 5 5 5 5

// ------------------ move ------------------

// ------------------ array with class objects. ------------------


END_TEST;

二維和多維array

//----------------------- multiple div array example ----------------------
RUN_GTEST(ArrayTest, MatrixOrMultipleDiv, @);

// like plain 2D array
array<array<int, 5>, 5> mat1 = {
    1,2,3,4,5,
    1,2,3,4,5,
    1,2,3,4,5,
    1,2,3,4,5,
    1,2,3,4,5,
};

// construct with 1D arys.
array<int, 5> ary = {1};
array<array<int, 5>, 5> mat2 = { ary, ary, ary, ary, ary};

// just like plain 2D array, but can ommit some value some each div.
array<array<int, 5>, 5> mat3 = {
    array<int, 5>{ 1, 2, 3, 4, 5},
    array<int, 5>{ 1, 2, 3, 4},
    array<int, 5>{ 1, 2, 3},
    array<int, 5>{ 1, 2,},
    array<int, 5>{ 1, }
};

// util function to print matrix.
auto printMatrix = [] (const array<array<int, 5>, 5>& mat) {
    for (const auto& ary : mat) {
        for (const auto& item : ary) {
            cout << item << " ";
        }
        cout << endl;
    }
};

pcln("ma1");
printMatrix(mat1);

pcln("mat2");
printMatrix(mat2);

pcln("mat3");
printMatrix(mat3);

END_TEST;

output:


************************ma1*********************
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
************************mat2*********************
1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
1 0 0 0 0
************************mat3*********************
1 2 3 4 5
1 2 3 4 0
1 2 3 0 0
1 2 0 0 0
1 0 0 0 0

注意事項

  • swap的代價:O(n)

  • 基本型別的預設構造是垃圾值,用初始化列表來避免

array<int, 3> a;            // no.
array<int, 3> a = {};       // good.

原始碼

作者水平有限,對相關知識的理解和總結難免有錯誤,還望給予指正,非常感謝!

歡迎訪問github部落格,與本站同步更新