1. 程式人生 > >有關模板類----實現Vector的類模板

有關模板類----實現Vector的類模板

由上一篇的文章中 ,我們已經知道,模板是一個程式碼生成器,可以極大效率提高我們的工作效率,實現讓編譯器為我們產生程式碼的機制

在C++面向物件中,我們在對類的使用上,已經達到了最少80%了,因此,模板類的使用,將極大的提高程式的執行效率,節省程式的執行時間。

(一)概念區分

類模板也稱類屬類或類生產類,允許使用者為類定義一種模式,使得類中的某些資料成員,默寫成員函式的引數,某些成員引數的返回值,能夠取任意型別(包括系統預定義和使用者自定義),如果一個類中資料成員的資料型別不能確定,或者是某個成員函式的引數返回值的型別不能確定,就必須將此類宣告為模板,他的存在不是代表一個具體的,實際的類,而是代表一類類。

類模板由三種類型的模板引數:型別模板引數,非型別模板引數和模板的模板引數。

模板類:是類模板例項化的的一個產物,可以從類模板派生出新的類,既可以派生類模板,也可以派生非類模板,模板類的重點是類表示由一個模板生成而來的類,模板類與平臺無關,具有可移植性,可用於基本資料型別,可用來建立動態增長和減小的資料結構。

模板類的例項化:只要有一種不同的型別,編譯器就會例項化出一個對應的類。

(二)有關Vector模板的實現

Vector:是用來連續的儲存資料的,類似於順序表,在其中任意位置的插入或者刪除的效率比較低,但是對記憶體碎片存在的就比較少,在某些情況下,對記憶體的使用率比較好。

#define _CRT_SECURE_NO_WARNINGS 1

//注:memcpy會出現淺拷貝的問題,因此在拷貝元素時,應該慎用

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
#include<assert.h>

using namespace std;

template<typename T>
class Vector
{
public:
	Vector()//建構函式,順序表為空
		:_start(0)
		, _finish(0)
		, _endOfStorage(0)
	{}

	Vector(const T*arr, size_t size);//宣告,順序表中已經存放元素

	Vector(const Vector& t) //拷貝建構函式
	{
		size_t size = t.Size();
		_start = new T[size];
		for (size_t i = 0; i < size; ++i)
		{
			_start[i] = t._start[i];
		}
		_finish = _start + size;
		_endOfStorage = _finish;
	}

	Vector<T>&operator=(const Vector<T>&v)//賦值運算子的過載
	{
		if (this != &v)
		{
			size_t size = v.Size();
			_start = new T[size];
			for (size_t i = 0; i < size; ++i)
			{
				_start[i] = v._start[i];
			}
		}
		return*this;
	}

	void PushBack(const T& data)//尾插,注意在此處,const修飾的是data
	{
		CheckCapacity();
		*_finish++ = data;//改變_finish的位置,從而將資料插入在上面
	}

	void PopBack()//尾刪,直接改變順序表中_finish的位置
	{
		--_finish;
	}

	void Insert(size_t pos, const T& data)//任意位置的插入
	{
		//檢查pos是否合法
		assert(pos < Size());//檢查當前插入的位置是否有效
		CheckCapacity();
		CheckCapacity();
		size_t i = 0;
		//搬移元素
		for (i = Size(); i > pos; --i)
		{
			_start[i] = _start[i - 1];
		}
		_start[pos] = data;
		++_finish;
	}

	void Erase(size_t pos)//任意位置刪除
	{
		assert(pos < Size());
		size_t i = 0;
		for (i = pos; i < Size() - 1; ++i)//減1的原因是:防止拿到最大元素處的後一個元素
		{
			_start[i] = _start[i + 1];
		}
		--_finish;
	}

	size_t Size()const//元素的個數
	{
		return _finish - _start;
	}

	size_t Capacity()const//容量
	{
		return  _endOfStorage - _start;
	}

	bool Empty()const//判斷順序表是否為空
	{
		return (Size() == 0);//_start==_finish;
	}

	void Resize(size_t newSize, const T& data = T())//將順序表中的size改變到newSize
	{
		size_t OldSize = Size();//將順序表中的size做一個記錄,從而方便下面的比較
		size_t capacity = Capacity();
		if (newSize < OldSize)
		{
			_finish = _start + newSize;//改變裡面儲存空間的大小
		}
		else if ((OldSize < newSize) && (newSize < capacity))
		{
			for (size_t i = OldSize; i < newSize; ++i)//將需要拷貝的資料拷貝下來
			{
				_start[i] = data;
			}
			_finish = _start + newSize;
		}
		else//此種情況為,新的順序表的大小,比原來順序表中的空間都大,因此需要開闢新的空間
		{
			T*tmp = new T[newSize];
			for (size_t i = 0; i < OldSize; ++i)//先拷貝舊空間的元素
			{
				tmp[i] = _start[i];
			}
			for (size_t j = OldSize; j < newSize; ++j)//將新的元素填充剩下的這段空間
			{
				tmp[j] = data;
			}
			delete[]_start;
			_start = tmp;
			_finish = _start + newSize;
			_endOfStorage = _start + newSize;
		}
	}


	T& operator[](size_t index)//下標運算子過載(成對出現)隨機訪問
	{
		return _start[index];
	}
	const T& operator[](size_t index)const
	{
		return _start[index];
	}

	T& Front()//返回最開始的元素
	{
		return *_start;//_start表示指向首元素的指標,對其解引用表示取出首地址的內容
	}
	const T& Front()const
	{
		return *_start;
	}

	T& Back()
	{
		return *(_finish - 1);//同Front
	}

	const T& Back()const
	{
		return *(_finish - 1);
	}

	void Clear()//置空順序表
	{
		_finish = _start;
	}

	template<class T>//不是類的成員函式
	friend ostream&operator<<(ostream&_cout, const Vector<T>&t)//輸出運算子過載
	{
		for (size_t i = 0; i < t.Size(); ++i)
		{
			_cout << t[i] << " ";
		}

		//memcpy(_Start, t._start, sizeof(t));
		return _cout;
	}

	void Display()
	{
		size_t size = Size();
		for (size_t i = 0; i < size; ++i)
		{
			cout << _start[i];
			cout << " ";
		}
		cout << endl;
	}

	~Vector()//解構函式
	{
		if (_start != NULL)
		{
			delete[]_start;
			_start = NULL;
			_finish = NULL;
			_endOfStorage = NULL;

		}
	}

private:
	void CheckCapacity()
	{
		size_t size = Size();
		size_t capacity = Capacity();
		if (size >= capacity)
		{
			size_t newCapacity = capacity * 2 + 3;
			//申請新空間
			T*_tmp = new T[newCapacity];
			//拷貝元素
			for (size_t i = 0; i < size; ++i)
			{
				_tmp[i] = _start[i];
			}

			//釋放舊空間
			if (_start)//防止釋放的空間為空,從而導致程式崩潰
				delete[] _start;
			_start = _tmp;
			_finish = _start + size;
			_endOfStorage = _start + newCapacity;
		}
	}
private:
	T*_start;  //表示順序的開始,空間起始位置
	T*_finish;  //表示順序表中儲存元素的多少
	T*_endOfStorage; //表示順序表的容量
};

template<typename T>
Vector<T>::Vector(const T*arr, size_t size)//有元素,1、開闢大小 2、複製資料
:_start(new T[size])
, _finish(_start + size)
, _endOfStorage(_start + size)
{
	//memcpy(_start, arr, sizeof(T)*size)//會存在淺拷貝的問題
	for (size_t i = 0; i < size; ++i)//拷貝元素
	{
		_start[i] = arr[i];
	}
}


class String
{
public:
	String(const char* pStr = "")
	{
		if (_pStr == NULL)
		{
			_pStr = new char[1];
			(*_pStr) = '\0';
		}
		else
		{
			_pStr = new char[strlen(pStr) + 1];
			strcpy(_pStr, pStr);
		}
	}

	/*String(const String &s)//拷貝建構函式
	:_pStr(NULL)
	{
	String tmp(s._pStr);
	swap(_pStr, tmp);
	}*/

	String(const String &s)
		:_pStr(new char[strlen(s._pStr) + 1])
	{
		strcpy(_pStr, s._pStr);
	}

	String& operator=(const String&s)
	{
		if (this != &s)//檢測是不是自己給自己賦值
		{
			char*tmp = new char[strlen(s._pStr) + 1];
			strcpy(tmp, s._pStr);

			delete[] _pStr;
			_pStr = NULL;
			_pStr = tmp;
		}
		return *this;
	}
	friend ostream&operator<<(ostream&_cout, const String&t)//輸出運算子過載
	{
		_cout << t._pStr;
		return _cout;
	}

	~String()
	{
		if (_pStr != NULL)
		{
			delete[] _pStr;
			_pStr = NULL;
		}
	}

private:
	char*_pStr;
};

#if 0
void FunTest1()
{
	//Vector<int> s1;
	int arr[] = { 1, 2, 3, 4 };
	Vector<int> s2(arr, sizeof(arr) / sizeof(arr[0]));
	//Vector<int> s3(s2);
	//cout << s2 << endl;
	//s1 = s2;
	//cout << s1 << endl;
	cout << "size= " << s2.Size() << endl;
	cout << "capacity= " << s2.Capacity() << endl;
	cout << s2 << endl;
	s2.Display();

	s2.PushBack(5);
	s2.PushBack(6);
	s2.PushBack(7);
	cout << "size= " << s2.Size() << endl;
	cout << "capacity= " << s2.Capacity() << endl;
	cout << s2 << endl;
	s2.Display();

	s2.PopBack();
	s2.PopBack();
	cout << "size= " << s2.Size() << endl;
	cout << "capacity= " << s2.Capacity() << endl;
	cout << s2 << endl;
	s2.Display();

	s2.Erase(2);
	cout << "size= " << s2.Size() << endl;
	cout << "capacity= " << s2.Capacity() << endl;
	s2.Insert(3,7);
	cout << "size= " << s2.Size() << endl;
	cout << "capacity= " << s2.Capacity() << endl;
	cout << s2 << endl;
	s2.Display();

	s2.Resize(4);
	cout << "size= " << s2.Size() << endl;
	cout << "capacity= " << s2.Capacity() << endl;
	s2.Display();

	s2.Resize(30);
	cout << "size= " << s2.Size() << endl;
	cout << "capacity= " << s2.Capacity() << endl;
	cout << s2 << endl;
	s2.Display();
}
#endif
void FunTestString()
{
	Vector<String> s;
	s.PushBack("1111");
	s.PushBack("2222");
	s.PushBack("3333");
	cout << "size= " << s.Size() << endl;
	cout << "capacity= " << s.Capacity() << endl;
	cout << s << endl;

	s.PushBack("4444");
	cout << "size= " << s.Size() << endl;
	cout << "capacity= " << s.Capacity() << endl;
	cout << s << endl;

	s.PopBack();
	cout << "size= " << s.Size() << endl;
	cout << "capacity= " << s.Capacity() << endl;
	cout << s << endl;

	s.Insert(2, "5555");
	cout << "size= " << s.Size() << endl;
	cout << "capacity= " << s.Capacity() << endl;
	cout << s << endl;

	s.Resize(4);
	cout << "size= " << s.Size() << endl;
	cout << "capacity= " << s.Capacity() << endl;
	cout << s << endl;

	s.Resize(20,"0000");
	cout << "size= " << s.Size() << endl;
	cout << "capacity= " << s.Capacity() << endl;
	cout << s << endl;

	s.Clear();
	cout << "size= " << s.Size() << endl;
	cout << "capacity= " << s.Capacity() << endl;
	cout << s << endl;
}

int main()
{
	FunTestString();
	system("pause");
	return 0;
}
由上面可以看出,由於在模板中對於輸出運算子的過載,存在著一定的問題,因此上面對於String型別與內建型別分別測試。

結果如下:

String型別


內建型別:

相關推薦

有關模板----實現Vector模板

由上一篇的文章中 ,我們已經知道,模板是一個程式碼生成器,可以極大效率提高我們的工作效率,實現讓編譯器為我們產生程式碼的機制 在C++面向物件中,我們在對類的使用上,已經達到了最少80%了,因此,模板類的使用,將極大的提高程式的執行效率,節省程式的執行時間。 (一)概念區分

實現的抽象方法,父呼叫抽象方法

首先,父類有一個抽象方法,並且在自身的其他方法中呼叫了 public abstract class Baba { public abstract void say(String a); public void dosomething(String a){ say(a); }

動態記憶體管理allocatorC++ STL標準模板vector實現

//vector.h #ifndef NSTL_VECTOR_H_ #define NSTL_VECTOR_H_ #include <memory> #include <string> namespace nstl { class vector { public

c++中模板實現模板模板函數)

c++ 模板實例化 泛型編程 [TOC] 模板  當我們實現一個交換函數時,我們可以寫成如下。 void Swap(int& x, int& y) { int tmp = x; x = y; y = tmp; }  這裏只能交換兩個整

後臺開發閱讀筆記——STL(標準模板庫)的使用之string實現

string類的實現: class String { public: String(const char *str=null); String(const String &other); ~String(); String &operator =(const

C++模板實現hashMap

非常常見的hashMap,具體介紹可以自己百度,這裡就不詳細介紹了 主要採用的是拉鍊法,然後hash函式採用的是C++自帶的hash函式庫   標頭檔案 1 #ifndef MAP_H 2 #define MAP_H 3 4 // Hash table impl

二叉堆模板實現以及使用它進行堆排序

二叉堆:二叉堆一棵完全二叉樹,從遞迴的定義來講,對於完全二叉樹的任何一個節點,其左孩子要麼是空樹要麼是一個完全二叉樹,右孩子同上。 堆:對於一個堆來講,可以是一個大根堆,也可以是一個小根堆。 大根堆的性質:對於在大根堆任何一個節點,其值不小於左右孩子的值。 小根堆的性質:對於在大

BST(二叉搜尋/排序樹)模板實現

BST樹的遞迴定義: (1)BST樹是一棵空樹。 (2)BST樹由根節點、左子樹和右子樹。左子樹和右子樹分別都是一棵BST樹。 由於二叉樹的遞迴定義,使得在二叉樹中許多操作中可以以遞迴的方式書寫操作,程式碼更加淺顯易懂。 重點條件:左子樹中的所有節點的資料域都小於或等於根節點的資

STL典型使用--vector模板

vector是將元素放在一個動態陣列中加以管理的容器,vector可以隨機存取元素(用[]操作符或at()直接存取),也支援迭代器存取元素;vector在尾部新增或移除元素十分高效,但是在中間或者頭部插入/移除元素會比較費時。 1. 增加/刪除vector內的元

資料庫配置excel匯出模板,poi匯出工具實現過程

1.配置模板id,檔名字尾,本地儲存位置 2.配置模板表頭,資料位於表格的列號,對應實體的欄位,型別 3.儲存匯出的記錄,參考頁面欄位設計 4.後端呼叫部分設計參考 箭頭標明泛型匯出實體類T,匯出工具類例項化,執行緒排程 5.工具類主要設計 表頭資料查詢並寫入表頭 6.資料庫查詢

C++模板實現模板函式和模板,附帶模板實現順序表和連結串列程式碼)

模板  當我們實現一個交換函式時,我們可以寫成如下。 void Swap(int& x, int& y) { int tmp = x; x = y; y = tmp; }  這裡只能交換兩個整數,當我們

用c++實現 c++單鏈表的實現(採用模板

函式實現資料的插入(頭插&&尾插)、刪除(頭刪&&尾刪)、查詢、按值插入、按值刪除、求長、單鏈表清除、單鏈表摧毀、資料的逆置以及資料排序 main函式 #include"List.h"//單鏈表 void main() { List&l

二叉樹的模板實現

二叉樹的定義 二叉樹(Binary Tree)是n>0個結點的有限集合,該集合或者為空集(稱為空二叉樹),或者由一個根節點和兩棵不相交的分別被稱為左子樹和右子樹組成。 ·不存在度大於2的結點; ·左右子樹是有序的,次序不能任意的顛倒。 二叉樹的

自己動手寫資料結構:二叉樹BinaryTree模板C++實現(功能較全)

#ifndef MYBINARYTREE_H #define MYBINARYTREE_H template <class T> class BinaryTree { protected: struct TNode { T val; TNode*

C++中用模板(結點,連結串列實現的單鏈表的合併操作!

程式碼通俗易通,如下 List.h #include<stdio.h> template <class T> class ListNode { T data; ListNode<T>* link; public:

棧與佇列-順序棧與鏈棧模板實現(資料結構基礎 第3周)

這是用C++編寫的棧的類模板的實現,包括順序棧和鏈棧,並進行了簡單的測試。 程式碼中srrStack類和lnkStack類均繼承於Stack類, Stack類可以看成是棧的邏輯結構(ADT抽象資料型別,Abstract Data Type)。注意這裡實現是棧與

簡單的C++委託 —— 用模板實現成員函式的回撥

template <class R, class P1, class P2>class IDelegate{public:virtual R Invoke(P1, P2) = 0;};template <class T, class R, class P1, class P2>clas

c++:利用模板實現氣泡排序

首先我們來明確函式模板與類模板的概念及其用法。 模板是一種對型別進行引數化的工具,通常有兩種形式------>函式模板和類模板。 函式模板針對僅引數型別不同的函式; 類模板針對僅資料成員和成員函式型別不同的類。 函式模板的格式:     template &l

資料結構——迴圈佇列(順序佇列)模板實現

資料結構筆記3.3 順序佇列是用順序表實現的(即依託於陣列),這裡實現的是迴圈佇列,其實也可以不用迴圈,但是那樣的話,空間的利用效率就太低了,這就是”假溢位”問題,因為在陣列的前端可能還有空閒的位置(因為佇列中的資料是在動態變化的,可能出隊也可能入對)