【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 = rValuerValue全部元素被移动到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博客,与本站同步更新

### C++ 中 `type_traits` 的使用方法及示例 #### 基本概念 `<type_traits>` 是 C++ 标准库的一部分,提供了一组模板类来查询和操作类型属性。这些工具允许程序员在编译期执行复杂的类型检查、转换和其他元编程任务。 #### 类型特征检测 通过 `<type_traits>` 可以轻松实现不同类型特性的检测。例如: - 判断两个类型的相等性可以利用 `std::is_same<T,U>::value` 来完成[^1]。 ```cpp #include <iostream> #include <type_traits> template <typename T> void checkType() { if (std::is_same<T, int>::value) { std::cout << "T type is int\n"; } else { std://cout << "T type is not int\n"; } } ``` 此代码片段展示了如何基于传递给模板参数的具体类型做出不同行为的选择。 #### 添加额外功能 除了简单的类型比较外,还可以做更多事情,比如验证某个类型是否有特定成员函数或数据成员存在。这通常涉及到 SFINAE 技术的应用[^3]。 定义宏辅助创建自定义类型特质如下所示: ```cpp #define DEFINE_TYPE_TRAIT(name, func)\ template<typename T>\ struct name {\ template<typename Class> \ static constexpr bool test(decltype(&Class::func)*);\ template<typename> \ static constexpr bool test(...);\ \ static constexpr bool value = test<T>(0);\ };\ // 定义具体实例化版本 DEFINE_TYPE_TRAIT(has_func_foo, foo) int main(){ struct A{}; struct B{ void foo(); }; std::cout<<has_func_foo<A>::value<<"\n"; // 输出 0 表明A没有foo() std::cout<<has_func_foo<B>::value<<"\n"; // 输出 1 表明B有foo() return 0; } ``` 这段程序说明了怎样构建自己的类型特性测试器,并应用于实际场景之中。 #### 高级应用案例 当结合其他 STL 组件一起工作时,`type_traits` 更加体现出其强大之处。考虑下面的例子,在容器内部迭代过程中动态调整元素大小: ```cpp #include <vector> #include <string> #include <memory> #include <type_traits> template<class Container> auto resizeElements(Container& c, size_t newSize){ using ValueType=typename Container::value_type; if constexpr(std::is_pointer_v<ValueType>){// 如果是智能指针或其他指向对象的原始指针,则不改变它们所指向的对象尺寸 // Do nothing since we don't want to modify what pointers point at. }else{ for(auto&& elem : c){ if constexpr(!std::is_void_v<std::decay_t<decltype(*elem)>> && !std::is_array_v<std::decay_t<decltype(*elem)>>){ *elem.resize(newSize); } } } } int main(){ std::vector<std::unique_ptr<int[]>> vecPtrs={new int[5], new int[7]}; std::vector<std::string> vecStrings={"hello", "world"}; resizeElements(vecPtrs, 8); // 不会修改数组长度 resizeElements(vecStrings, 10); return 0; } ``` 这里展示了一个通用化的 `resizeElements()` 函数,它可以处理多种情况下的容器内元素重设逻辑,而无需担心破坏原有结构的数据一致性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值