数组的新玩法,创建数组类。

数组的新玩法,创建数组类。

C语言中的数组不好用吗?为什么需要数组类?仔细思考一下,原生数组有缺点吗?额,还真有!在使用数组的时候,使用者想要获取数组的长度是比较麻烦,如果不清楚函数参数传递的规则,使用函数来求数组长度,得到的必然是错误的结果。比如:

#include <iostream>
using namespace std;

int array_length(int a[])
{
	return sizeof(a)/sizeof(*a);
}

int main(int argc, const char* argv[])
{
	int a[5] = {0};

	cout << array_length(a) << endl;	// 结果是1
	
	return 0;
}

输出奇怪,原因何在?为什么传了个数组进函数就不对了呢?这就要讲讲函数参数传递的原理了,一般的参数都是直接在内存里拷贝一份出来,然后在函数体里面随意使用,但是使用数组作为形参就特别一点了,编译器会化繁为简,直接将数组转换为指针进行传递。是不是没有办法求数组的长度了呢?NO,来看看强大的宏定义:

#include <iostream>
using namespace std;

#define array_length(a) sizeof(a)/sizeof(*a)

int main(int argc, const char* argv[])
{
	int a[5] = {0};

	cout << array_length(a) << endl;

	return 0;
}

宏定义确实强大的一笔。但是宏定义是由预处理器处理的,编译器并不知道宏的存在,使用不当容易出错。

讲了一大堆原生数组的东西,现在直奔主题,数组类,用面向对象的思想实现的一个类。提供各种方法使得数组类拥有原生数组的各种操作。比如数组操作符[]的使用,这就很方便了,并且还能对下标参数进行越界检查,如果参数异常则默认使用 throw(0) 抛一个0,(补个小知识点:编译器对原生数组下标越界是摊手的,但程序可能会崩掉)。然后就是提供设置目标元素、获取目标元素以及获取数组长度的方法。

话不多说,上代码:


#ifndef ARRAY_H
#define ARRAY_H

template <typename T>	// 使用模板,接收任意类型的参数
class Array
{
protected:
	T* m_array;	// 成员变量是指针,指向数组首元素
public:
	bool set(int i, const T& value)
	{
		bool ret = (0 <= i) && (i < length());
		
		if( ret )
		{
			m_array[i] = value;
		}
		else
		{
			throw(0);
		}

		return ret;
	}
	
	bool get(int i, const T& value) const
	{
		bool ret = (0 <= i) && (i < length());
		
		if( ret )
		{
			value = m_array[i];
		}
		else
		{
			throw(0);
		}
		
		return ret;
	}
	
	T get(int i) const 
	{
		T ret;
		
		if( !get(i,ret) ) // 如果获取元素失败,抛异常
		{
			throw(0);
		}
		
		return ret;
	}

	T& operator [] (int i)
	{
		if( (0 <= i) && (i < length()) )
		{
			return m_array[i];
		}
		else
		{
			throw(0);
		}
	}
	
	T operator [] (int i) const
	{
		if( (0 <= i) && (i < length()) )
		{
			return m_array[i];
		}
		else
		{
			throw(0);
		}
	}

	T* array()	// 返回数组首元素地址,可用于排序等操作
	{
		return m_array;
	}
	
	virtual int length() const = 0;	// 声明为纯虚函数,作为基类只用于被继承而不能实列化
	
};

#endif

数组基类就创建完成了,对比着原生数组来做思考,我们需要怎么样的数组类呢?原生数组在定义的时候就已经确定长度,之后便再也无法修改,这是不是有点不方便呢,我们就对这做一点点的改进,提供两个数组类,一个是固定大小的,另一个则是可以动态决定大小的。这样选择可就丰富了!先来个简单的静态数组类压压惊。

#ifndef STATICARRAY_H
#define STATICARRAY_H

#include "Array.h"

template <typename T, int N>	// 使用模板
class StaticArray : public Array<T> 
{
protected:
	T array[N];	// 实际所使用的空间
public:
	StaticArray()
	{
		this->m_array = array;	// 交出使用权
	}

	StaticArray(const StaticArray<T,N>& obj)	// 拷贝构造函数,使用自身的空间,但是用别人的内容
	{
		this->m_array = array;
		
		for(int i = 0; i < this->length(); ++ i)
		{
			this->m_array[i] = obj[i];
		}
	}
	
	StaticArray<T,N>& operator = (const StaticArray<T,N>& obj)
	{
		if( this != &obj )	// 自赋值就没必要了
		{
			for(int i = 0; i < this->length(); ++ i)
			{
				this->m_array[i] = obj[i];
			}
		}
	}
	int length() const
	{
		return N;
	}
};

#endif

静态数组类就完成了哦,马上来使用一下!

#include <iostream>
#include "StaticArray.h"

using namespace std;

int main(int argc, const char* argv[])
{
	StaticArray<int,5> sa;
	
	for(int i = 0; i < 5; ++ i)
	{
		sa[i] = i * i;
	}
	
	for(int i = 0; i < 5; ++ i)
	{
		cout << sa.get(i) << endl;
	}

	return 0;
}

输出结果

完成了静态数组类啦,正所谓高下相盈,那就一鼓作气实现动态数组类吧。首先分析,何为动态,如何实现动态,其实作为一名学过C语言的人儿,心照不宣地想到堆空间的动态申请和动态释放。没错,动态数组类的实现要点就是从堆空间申请一片内存空间,当动态数组类对象觉得空间太小了,已经不够用了,那就重新申请一块大得堆空间内存,(!!!注意:必须释放之前的堆空间,内存泄漏极其危险),如果觉得太大。。。。也一样的操作。好了,上代码:

#ifndef DYNAMICARRAY_H
#define DYNAMICARRAY_H

#include "Array.h"

template <typename T>
class DynamicArray : public Array<T>
{
protected:
	int m_length;	
public:
	DynamicArray((int len = 0)
	{
		T* array = new T(len);	// 向堆空间动态申请一片内存
		
		if( this->m_array != NULL )	// 申请成功
		{
			this->m_array = array;
			this->m_length = len;
		}
		else
		{
			throw(0);	// 申请失败,完事了
		}
	}
	
	DynamicArray(const DynamicArray<T>& obj)	// 拷贝构造函数
	{
		array = new T(obj.length());	// 重新申请一段内存

		if( array != NULL )
		{
			this->m_array = array;
			this->m_length = obj.length();

			for(int i = 0; i < obj.length(); ++ i)
			{
				this->m_array[i] = obj[i];
			}
		}
	}

	DynamicArray<T>& operator = (const DynamicArray<T>& obj)
	{
		if( this != &obj )	// 杜绝自赋值
		{
			T* tem = obj.m_array;	// 安全起见,保留一下旧地址
			T* array = new T(obj.length());
			
			if( array != NULL )
			{
				this->m_array = array;
				this->m_length = obj.length();

				for(int i = 0; i < obj.length(); ++ i)
				{
					this->m_array[i] = obj[i];
				}
			}
			
			obj.m_array = NULL;
			delete[] tem;
		}
	}
	
	void resize(int newlen)	// 实现动态数组的关键函数
	{
		T* tem = this->m_array;
		int size = (this->m_length < newlen) ? this->m_length : newlen;
		// 使用三目运算符计算需要拷贝的内容大小
		T* array = new T(newlen);
		
		if( array != NULL )
		{
			for(int i = 0; i < size; ++ i)
			{
				array[i] = this->m_array[i];
			}
		}
		else
		{
			throw(0);
		}
		
		delete[] tem;	// 很重要,把之前的内存空间释放
	}
	
	int length() const
	{
		return m_length;
	}
};

#endif

动态数组的玩法如下:

#include <iostream>
#include "DynamicArray.h"

using namespace std;

int main(int argc, const char* argv[])
{	
	DynamicArray<int> da(5);
	
	cout << "First length : " << da.length() << endl;
	
	for(int i = 0; i < 5; ++ i)
	{
		da[i] = i * i;
	}

	cout << endl;

	for(int i = 0; i < 5; ++ i)
	{
		cout << da.get(i) << " ";
	}
	
	da.resize(10); 	// 扩大一倍	
	
	cout << "Second length : " << da.length() << endl;

	for(int i = 0; i < 10; ++ i)
	{
		da[i] = i;
	}
	
	cout << endl;
	
	for(int i = 0; i < da.length(); ++ i)
	{
		cout << da[i] << " ";
	}

	return 0;
}

输出结果:
在这里插入图片描述

仅仅是一维数组好像不够酷!认真的?那就实现二维数组咯,温故知新,先回想一下C语言中的二维数组,形式确实是二维数组,但本质还是一维数组,只不过变成了数组的数组,二维数组在内存的排布是按照一个个大小相同数组顺序存储,所以利用数组类来实现二维数组就不难了,上代码:

#include <iostream>
#include "DynamicArray.h"

using namespace std;

int main(int argc, const char* argv[])
{
	DynamicArray< DynamicArray<int> > dda(5); // 相当于五行
	// 由此可知,动态数组的构造函数必须有默认值,默认值为0
	for(int i = 0; i < dda.length(); ++ i)
	{
		dda[i].resize(5); 		 // 相当于五列
	} 

	for(int i = 0; i < dda.length(); ++ i)
	{
		for(int j = 0; j < dda.length(); ++ j)
		{
			dda[i][j] = i + j;
		}
		
		cout << endl;
	}
	
	cout << endl;
	
	for(int i = 0; i < dda.length(); ++ i)
	{
		for(int j = 0; j < dda.length(); ++ j)
		{
			cout << " dda[" << i << "][" << j << "] = " << dda[i][j] << " ";
		}
	}
	
	return 0;
}

二维数组的输出还是挺酷的嘛。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值