观念提升--从数组到节点池
观念提升--从数组到节点池(手写vector,实际上是静态的)
以往,我们都是这样来存储一些数据的:
#include<iostream>
using namespace std;
#define N 1005
int a[N];
int main(){
int n;
cin>>n;
for (int i=1;i<=n;i++){
cin>>a[i];
}
return 0;
}
我们开了一个内存大小为N(1005)的数组,通过输入n 来控制要输入几个数据,但是,这么做大问题是没有的,但是小问题就在于可能会造成空间浪费,那么,我们用另一种思路想想看,是不是我需要一个储存空间就申请一个储存空间,这样就不太会浪费空间呢,也许是的吧。
那么,我们就可以想象,这里有一盆水,我要喝水就从里面舀,那我就可以形象的称水盆为节点池,那么这舀出来的水就是节点,话不多说,我们来实现一下:
int p;
在全局变量中,我们申请一个p,此时,p的值为int 的默认值0
tips:如何查看各类型的默认值
#include<iostream>
using namespace std;
int main(){
cout<<int()<<endl;//查看int类型默认值,以下以此类推
cout<<double()<<endl;
cout<<string()<<endl;
//system("pause");可以不加
return 0;
}
运行结果如图:
可以见的,int,double的默认值都是0,但是string复合类型的输出竟然是....怎么说呢,啥也不是。但其实他是一个空字串,至于为什么会这样,后文会有提及,是跟一个叫构造函数的东西有关.
回到正题,节点池已经建好了,那我们就申请节点吧。
int np=++p;
是的,就这么简单,但必须是++p;不然是先给p+1之前的值,关于详细信息末尾给出了链接 ,以上内容纯粹是为了给后面铺垫,下面才是正题
同时,我们还要认识一下一个叫做结构体的东西,他和class差不多,但总体上,比class好用(个人觉得)
基本语法如下
struct Node{
};
//注,Node可以自行替换,随意起的类名,分号要特别注意
通过之前的经验,我们知道,a[i]=x,是表示对a[i]位置进行赋值填充为x,那么我们的赋值语句也是如此
a[np]=x
现在,我们将结构体和以上结论结合起来:
#include<iostream>
using namespace std;
#define N 1005
template<class T>//函数模板
struct Vector{
T a[N];
int len;
Vector(){len=0};//这就是构造函数
/*
构造函数是什么呢?
这么说:当你的这个类被调用的时候,里面的初始值可能无法控制,又或者你要设置一些初始值,那 么就需要构造函数了,只要你调用类,它就会自动生成函数内的数据或执行其中的代码
*/
Vector (int newLen,int val=0){
resize(newlen,val);
}
void push_back(T val){
a[len]=val;
len++;
}
void pop_back(){
len--;
}
void set(int i,T val){//修改i位置的数据为val
a[i]=val;
}
T at (int i){//取第i位置的数据
return a[i];
}
T front(){
return a[0];
}
T back(){
return a[len-1];
}
int insert(int i,T d){//在第i位置插入数值d
for (int j=len;j>i;--j){
a[j]=a[j-1];
}
a[i]=d;
len++;
}
int erase(int i){
for (int j=i;j<len-1;j++){
a[j]=a[j+1];//覆盖
}
len--;//删除了一个节点,总长度减小
}
int size(){
return len;
}
bool empty(){
return len==0;
}
void clear(){
len=0;
}
//以下不用太在意,在下一期中会讲解
void BubbleSort(){
for (int i=0;i<len-1;i++){
for (int j=0;j<len-1-i;j++){
if (a[j]>a[j+1]){
swap(a[j],a[j+1]);
}
}
}
}
T& operator [](int i)
{
return a[i];
}
void resize(int newLen,int val=0){
if (newLen>len){
for (int i=len;i<newLen;++i)
a[i]=val;
}
len=newLen;
}
};
void show(Vector<int> vec){
for (int i=0;i<vec.size();++i){
cout<<vec[i]<<' ';
}
cout<<endl;
}
int main(){
Vector<int> vec;
for (int i=0;i<vec.size();++i){
cout<<vec[i]<<' ';
}
// for (int i=1;i<=5;i++){
// vec.push_back(i);
// }
// show(vec);
// vec.insert(3,9);
// cout<<vec.at(3)<<endl;
// vec.erase(2);
// cout<<vec.size()<<endl;
// show(vec);
// vec.BubbleSort();
// show(vec);
system("pause");
return 0;
}
关于详细信息末尾给出了链接
一个结构体也是可以有多个构造函数的,灵活运用会很方便,但是要求每个构造函数内的形参表不能一样,例:
#include <cstdio> struct node{ int x; node(int z):x(z){} node():x(1){} }; int main() { node a; node b(5); printf("%d %d\n",a.x,b.x); }
结果如图:
可见,如果你不调用具有形参的构造函数的话,就默认为你设的初值,这也很好的解释了上面的各类型默认值问题
最后你调用的时候需要
类名<类型> 对象名;
大功告成。另外,附上几篇可供参考的文章,如果有不懂,可以私聊我或评论,谢谢支持!
c++中struct构造函数_struct 构造函数-CSDN博客
C++函数模板的详细讲解【函数模板的概念、用法及其模板函数的概念知识】_c++函数模板的定义及使用-CSDN博客
C++运算符重载,前置自增(前置++)和后置自增(后置++)代码分析_运算符重载后置++-CSDN博客
顺便也可以看看函数重载,就在上面的博文,这里也给出博主的笔记:
函数模板
template<class 模板形参1,class模板形参2,...>
函数返回值 函数名(形参列表)
template <class T>
T add(T a, T b){
return a+b;
}
类模板(class=typename)
template<class 模板形参1,class 模板形参2>
struct 类名{
};
template<class T>
struct Pair{
T x, T y;
};
实例化
类名<类型名> 对象名;
该对象的类型为:类名<类型名>
*/
/*
重载运算符
T& operator [](int i)
{
return a[i];
}
*/