C++STL容器篇array的手动实现

前段时间写了一个通过C++ array类简单了解C++容器初始化机制_Harris_ayaka的博客-CSDN博客,现在我整理一下C++ array中具体细节。

详情参考array 类(C++ 标准库) | Microsoft Docs

array类
首先是语法:

template <class Ty, std::size_t N>
class array;
接收两个模板参数,并且将数组的尺寸作为模板参数之一,因此两个不同大小但元素类型相同的的array将被视为不同的类型,例如:

array<int,5> a1;
array<int,10> a2;
将被视为不同的类型。

<array>成员
0、private成员

这是作为array容器的基础,一个是指向数组首地址的指针,一个是数组的size

template <class Type, size_t N>
class array {
private:
    Type* list;
    size_t size;
    .....
}
1、构造函数
visual studio 2022的文档有两个构造函数:

即是正常的构造函数与复制构造函数

array();
 
array(const array& right);
但参考我的上一篇通过C++ array类简单了解C++容器初始化机制_Harris_ayaka的博客-CSDN博客

我还加上了一个带参的构造函数:

array(const std::initializer_list<Type> &r);
用于使用大括号初始化array对象:

array<int,n> nums{ 1,2,3,};
//用这样的形式初始化必须重载一个参数带initializer_list的函数
下面介绍三种构造函数的具体实现细节:

首先是默认构造函数,不带任何参数

template <class Type, size_t N>
array<Type, N>::array() {
    size = N;
    list = new Type[N];
    _begin = list;
    _end = list + N;
}
在这里,我有一个还未解决的问题:

众所周知,如果一个变量声明在主函数外面,那么他是一个全局变量,并且这个变量会被自动初始化,代码如下

#include<iostream>
#include<array>
using namespace std;
array<int, 10> a;
int main() {
    for (int i = 0; i < 10; ++i) {
        cout << a[i] << endl;
    }
}
输出结果如图:

但是我在使用自定义的array类的时候,声明全局变量,并不会被初始化

#include"array.h"
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
using namespace ayaka;
array<int, 10> a;
int main() {
    for (int i = 0; i < 10; ++i) {
        cout << a[i] << endl;
    }
}
结果如下,可以看到全部都是奇怪的值。希望这个问题有大佬能够帮我解答一下: )

 接下来是复制构造函数:

复制构造函数不能采用简单的把另一个变量赋值给该类,详情可以去自行了解一下深复制与浅复制机制

template <class Type, size_t N>
array<Type, N> array<Type, N>::operator=(const array<Type, N>& right) {
    size = N;
    list = new Type[N];
    _begin = list;
    _end = list + N;//迭代器要用的组件,后文会详细介绍
    for (int i = 0; i < size; ++i) {
        list[i] = right.list[i];
    }
}
最后是能够使用初始化列表的构造函数:

template <class Type, size_t N>
array<Type, N>::array(const std::initializer_list<Type>& r) {
    size = N;
    list = new Type[N];
    _begin = list;
    _end = list + N;
    auto it = r.begin();
    for (size_t i = 0; i < r.size(); ++i) {
        list[i] = *it++;
    }
    if (N > r.size()) {
        for (size_t i = r.size(); i < N; ++i) {
            list[i] = Type();
        }
    }
}
2、析构函数
众所周知,析构函数的作用是销毁对象,我们先来看看如果不写析构函数,调用默认的析构函数效果如何

#include"array.h"
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
using namespace ayaka;
int main() {
    const size_t n = 10;
    array<int,n> nums{ 1,2,3,};
    
    array<int, 10> a(nums);
    a.~array<int, n>();
    for (int i = 0; i < 10; ++i) {
        cout << a[i] << endl;
    }
}
运行结果发现这个析构函数就是个摆设,运行结果如下:

因此有必要自己动手重写一些析构函数:

~array() {
    delete[]list;
    size = 0;
    _begin = nullptr;
    _end = nullptr;//释放数组占用的空间,并且将size归0
}
 3、迭代器
C++继承了C语言的指针,在array类中,指针和迭代器可以看做是同一个东西,因此也不用去重载++,--,+,-等运算符,C指针本身就支持这些运算

但为了和保证接口相同即使用iterator来调用迭代器,因此要使用typedef,具体方法如下:

public:
    typedef Type* iterator;
    iterator begin();
    iterator end();
private:
    iterator _end;
    iterator _begin;
在外部使用:

array<int, 5>::iterator it;
begin()与end()

template <class Type, size_t N>
Type* array<Type, N>::begin() {
    return _begin;
}
template <class Type, size_t N>
Type* array<Type, N>::end() {
    return _end;
}
//_begin是数组的首地址,_end是数组的尾地址
成员函数:
1、at()

template <class Type, size_t N>
Type& array<Type, N>::at(size_t off) {//不检查下标
    return*(list + off);
}
 
template <class Type, size_t N>
const Type& array<Type, N>::at(size_t off) const {
    return*(list + off);
}
2、assign()//C++ 11中已经过时,用fill替代,于是我就不写这个了

3、fill()

void fill(const Type& val);
template <class Type, size_t N>
void fill(const Type& val) {
    for (int i = 0; i < size; ++i) {
        list[i] = val;
    }
}
至于其他的

back():返回最后一个元素

front():返回第一个元素

empty():返回容器是否为空等等,感觉没有什么必要写

运算符重载
最重要的是"[]"和"="两个运算符,至于其他运算符尤其是比较运算符,我个人感觉实用性不强,而且实现原理简单所以没必要写。

1、[]运算符

//定义
Type& operator[](size_t i);
const Type& operator[](size_t i)const;
 
//实现直接利用上文提到的at函数,实现起来比较简单,又非常好用
template <class Type, size_t N>
const Type& array<Type, N>::operator[](size_t i)const {
    return at(i);
}
template <class Type, size_t N>
Type& array<Type, N>::operator[](size_t i) {
    return at(i);
}
2、=运算符

template <class Type, size_t N>
array<Type, N> array<Type, N>::operator=(const array<Type, N>& right) {
    for (int i = 0; i < size; ++i) {
        list[i] = right.list[i];
    }
    return *this 
}
测试一下:

#include"array.h"
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
using namespace ayaka;
int main() {
    const size_t n = 10;
    array<int, n> nums{ 1,2,3, };
    array<int, 10> a;
    a = nums;
    for (int i = 0; i < 10; ++i) {
        cout << a[i] << endl;
    }
}
运行结果:

得到了正确的结果

非成员函数
1、swap()

交换两个数组的首地址,并且迭代器也需要重新赋值

template <class Type, size_t N>//声明
friend void swap(array<Type, N>& left, array<Type, N>& right);
 
//定义
template <class Type, size_t N>
void swap(array<Type, N>& left, array<Type, N>& right) {
    Type* p = left.list;
    left.list = right.list;
    right.list = p;
    left._begin = left.list;
    right._begin = right.list;
    left._end = left.list + N;
    right._end = right.list + N;
}
测试一下

#include"array.h"
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
using namespace ayaka;
int main() {
    const size_t n = 10;
    array<int, n> nums{ 1,2,3, };
    array<int, n> a;
    a.fill(114514);
    swap(a, nums);
    for (int i = 0; i < n; i++) {
        cout << a[i] << endl;
    }
}
运行结果如下:

得到了正确的结果 

 2、get()

template <int i, class Type, size_t N>
Type& get(array<Type, N>& arr) {
    return arr[i];
}
 
template <int i, class Type, size_t N>
constexpr const Type& get(const array<Type, N>& arr)noexcept {
    return arr[i];
}
 
template <int i, class Type, size_t N>
constexpr Type&& get(const array<Type, N>& arr) noexcept {
    return arr[i];
}
使用方式如下:

#include"array.h"
#include<iostream>
#include<array>
using std::cout;
using std::cin;
using std::endl;
using namespace ayaka;
int main() {
    const size_t n = 10;
    array<int, n> nums{ 1,2,3, };
    array<int, 10> a;
    a = nums;
    int c = get<5>(a);
    cout << c << endl;
}
其中get<x>中的x必须是个常量。

总结:
这次通过手动实现array,了解了一些静态数组的机制,后面应该还会写一个在为array定义迭代器的过程中遇到的C++范围for循环的机制。辣鸡程序员,如有问题烦请各位大佬批评指正。
————————————————
版权声明:本文为CSDN博主「Harris_ayaka」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Harris_ayaka/article/details/125101566

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值