栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

手动模拟C++ stl中的array类

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

手动模拟C++ stl中的array类

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

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

array类

首先是语法:

template 
class array;

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

array a1;
array a2;

将被视为不同的类型。

成员

0、private成员

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

template 
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 &r);

用于使用大括号初始化array对象:

array nums{ 1,2,3,};
//用这样的形式初始化必须重载一个参数带initializer_list的函数

下面介绍三种构造函数的具体实现细节:

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

template 
array::array() {
	size = N;
	list = new Type[N];
	_begin = list;
	_end = list + N;
}

在这里,我有一个还未解决的问题:

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

#include
#include
using namespace std;
array a;
int main() {
	for (int i = 0; i < 10; ++i) {
		cout << a[i] << endl;
	}
}

输出结果如图:

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

#include"array.h"
#include
using std::cout;
using std::cin;
using std::endl;
using namespace ayaka;
array a;
int main() {
	for (int i = 0; i < 10; ++i) {
		cout << a[i] << endl;
	}
}

结果如下,可以看到全部都是奇怪的值。希望这个问题有大佬能够帮我解答一下: )

 接下来是复制构造函数:

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

template 
array array::operator=(const array& 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 
array::array(const std::initializer_list& 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
using std::cout;
using std::cin;
using std::endl;
using namespace ayaka;
int main() {
	const size_t n = 10;
	array nums{ 1,2,3,};
	
	array a(nums);
	a.~array();
	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::iterator it;

begin()与end()

template 
Type* array::begin() {
	return _begin;
}
template 
Type* array::end() {
	return _end;
}
//_begin是数组的首地址,_end是数组的尾地址
成员函数:

1、at()

template 
Type& array::at(size_t off) {//不检查下标
	return*(list + off);
}

template 
const Type& array::at(size_t off) const {
	return*(list + off);
}

2、assign()//C++ 11中已经过时,用fill替代,于是我就不写这个了

3、fill()

void fill(const Type& val);
template 
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 
const Type& array::operator[](size_t i)const {
	return at(i);
}
template 
Type& array::operator[](size_t i) {
	return at(i);
}

2、=运算符

template 
array array::operator=(const array& right) {
	for (int i = 0; i < size; ++i) {
		list[i] = right.list[i];
	}
    return *this 
}

测试一下:

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

运行结果:

得到了正确的结果

非成员函数

1、swap()

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

template //声明
friend void swap(array& left, array& right);

//定义
template 
void swap(array& left, array& 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
using std::cout;
using std::cin;
using std::endl;
using namespace ayaka;
int main() {
	const size_t n = 10;
	array nums{ 1,2,3, };
	array a;
	a.fill(114514);
	swap(a, nums);
	for (int i = 0; i < n; i++) {
		cout << a[i] << endl;
	}
}

运行结果如下:

得到了正确的结果 

 2、get()

template 
Type& get(array& arr) {
	return arr[i];
}

template 
constexpr const Type& get(const array& arr)noexcept {
	return arr[i];
}

template 
constexpr Type&& get(const array& arr) noexcept {
	return arr[i];
}

使用方式如下:

#include"array.h"
#include
#include
using std::cout;
using std::cin;
using std::endl;
using namespace ayaka;
int main() {
	const size_t n = 10;
	array nums{ 1,2,3, };
	array a;
	a = nums;
	int c = get<5>(a);
	cout << c << endl;
}

其中get中的x必须是个常量。

总结:

这次通过手动实现array,了解了一些静态数组的机制,后面应该还会写一个在为array定义迭代器的过程中遇到的C++范围for循环的机制。辣鸡程序员,如有问题烦请各位大佬批评指正。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/947966.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号