1. 程式人生 > >c++單鏈表【建構函式、運算子過載、解構函式、增刪查改等】

c++單鏈表【建構函式、運算子過載、解構函式、增刪查改等】

c++中的單向連結串列寫法:實現增刪查改、建構函式、運算子過載、解構函式等。

建立標頭檔案SList.h

#pragma once

typedef int DataType;
//SList要訪問SListNode,可以通過友元函式實現,友元函式在被訪問的類中
class SListNode
{
	friend class SList;//友元函式
public:
	SListNode(const DataType x)
		:_data(x)
		, _next(NULL)
	{}
private:
	SListNode* _next;
	DataType _data;
};

class SList
{
public:
	SList()
		:_head(NULL)
		, _tail(NULL)
	{}
	//深拷貝
	SList(const SList& s)
		:_head(NULL)
		, _tail(NULL)
	{
		SListNode* cur = s._head;
		while (cur)
		{
			this->PushBack(cur->_data);
			cur = cur->_next;
		}
	}
	////深拷貝的傳統寫法
	//SList& operator=(const SList& s)
	//{
	//	if (this != &s)
	//	{
	//		Clear();
	//		SListNode* cur = s._head;
	//		while (cur)
	//		{
	//			this->PushBack(cur->_data);
	//			cur = cur->_next;
	//		}
	//	}
	//	return *this;
	//}
	//深拷貝的現代寫法
	SList& operator=(SList& s)
	{
		swap(_head, s._head);
		swap(_tail, s._tail);
		return *this;
	}
	~SList()
	{
		Clear();
	}
public:
	void Clear();
	void PushBack(DataType x);
	void PopBack();
	void PushFront(DataType x);
	void PopFront();
	//void Insert(size_t pos,DataType x);
	void Insert(SListNode* pos, DataType x);
	void Erase(SListNode* pos);
	SListNode* Find(DataType x);
	void PrintSList();
private:
	SListNode* _head;
	SListNode* _tail;
};

各函式的實現

#include<iostream>
using namespace std;

#include"SList.h"
#include<assert.h>

void SList::Clear()
{
	SListNode* cur = _head;
	while (cur)
	{
		SListNode* del = cur;
		cur = cur->_next;
		delete del;
      del = NULL;
	}
}

void SList::PrintSList()//列印連結串列
{
	SListNode* cur = _head;
	while (cur)
	{
		cout << cur->_data << "->";
		cur = cur->_next;
	}
	cout << "NULL" << endl;
}

void SList::PushBack(DataType x)//尾插
{
	if (NULL == _head)
	{
		_head = new SListNode(x);//開闢一個值為x的新結點
		_tail = _head;
	}
	else
	{
		//SListNode* cur;
		//cur->_data = x;
	    //_tail->_next = cur;
		//_tail = cur;
		_tail->_next= new SListNode(x);
		_tail = _tail->_next;
	}
}

void SList::PopBack()//尾刪
{
	if (NULL == _head)
	{
		cout << "SList is empty!" << endl;
	}
	else if (_head == _tail)
	{
		delete _head;
		_head = _tail = NULL;
	}
	else
	{
		SListNode* cur = _head;//找到要刪除尾節點的前一個節點cur
		while (cur->_next->_next)
		{
			cur = cur->_next;
		}
		delete cur->_next;
		cur->_next = NULL;
		_tail = cur;
	}
}

void SList::PushFront(DataType x)//頭插
{
	SListNode* tmp = _head;
	_head=new SListNode(x);
	_head->_next = tmp;

}

void SList::PopFront()//頭刪
{
	if (NULL == _head)
	{
		cout << "SList is empty!" << endl;
	}
	else if (NULL == _head->_next)
	{
		delete _head;
		_head = NULL;//delete後要將指標設空,否則產生野指標
	}
	else
	{
		SListNode* tmp = _head->_next;
		delete _head; 
		_head = tmp;
	}
}

//void SList::Insert(size_t pos, DataType x)
//{
//	assert(pos);
//	SListNode* tmp = _head;
//	pos -= 1;
//	while (--pos)
//	{
//		tmp = tmp->_next;
//	}
//	if (NULL == tmp)
//		SList::PushBack(x);
//	else
//	{
//		SListNode* next = tmp->_next;
//		SListNode* cur = new SListNode(x);
//		tmp->_next = cur;
//		cur->_next = next;
//	}
//}

void SList::Insert(SListNode* pos, DataType x)////指定位置處插入x
{
	assert(pos);
	SListNode* tmp = _head;
	while (tmp)
	{
		if (NULL == tmp->_next)
			SList::PushFront(x);
		else if (pos == tmp->_next)
		{
			SListNode* cur = new SListNode(x);
			cur->_next= tmp->_next;
			tmp->_next = cur;
			return;//注意結束迴圈
		}
		tmp = tmp->_next;
	}
}

void SList::Erase(SListNode* pos)
{
	assert(pos);
	SListNode* tmp = _head;
	while (tmp)
	{
		if (NULL == tmp->_next)
			SList::PopFront();
		else if (pos == tmp->_next)
		{
			SListNode* cur = tmp->_next->_next;
			delete tmp->_next;
			tmp->_next = NULL;
			tmp->_next = cur;
			return;//注意結束迴圈
		}
		tmp = tmp->_next;
	}
}

SListNode* SList::Find(DataType x)
{
	SListNode* cur = _head;
	while (cur)
	{
		if (x == cur->_data)
		{
			return cur;
		}
		cur = cur->_next;
	}
	return NULL;
}

各操作的測試用例

void Test1()
{//尾插尾刪
	SList S;
	S.PushBack(1);
	S.PushBack(2);
	S.PushBack(3);
	S.PushBack(4);
	S.PrintSList();
	S.PopBack();
	S.PrintSList();
	//S.PopBack();
	//S.PopBack();
	//S.PrintSList();
	//S.PopBack();
	//S.PopBack();
	//S.PopBack();
	SList S1(S);
	S1.PrintSList();

	SList S2;
	S2 = S;
	S2.PrintSList();
}

void Test2()
{//頭插頭刪
	SList S;
	S.PushFront(1);
	S.PushFront(2);
	S.PushFront(3);
	S.PushFront(4);
	S.PrintSList();
	S.PopFront();
	S.PrintSList();

	S.PopFront();
	S.PopFront();
	S.PopFront();
	S.PrintSList();
	S.PopFront();
}

void Test3()
{//指定位置插入某數,查詢某數
	SList S;
	S.PushBack(1);
	S.PushBack(2);
	S.PushBack(4);
	S.PushBack(5);
	S.PrintSList();
	//S.Insert(3, 3);
	SListNode* p = S.Find(4);
	S.Insert(p, 3);
	S.PrintSList();
}

void Test4()
{//刪除某結點
	SList S;
	S.PushBack(1);
	S.PushBack(2);
	S.PushBack(3);
	S.PushBack(10);
	S.PushBack(4);
	S.PushBack(5);
	S.PrintSList();

	SListNode* p = S.Find(10);
	S.Erase(p);
	S.PrintSList();
}

友元函式

     在實現類之間資料共享時,減少系統開銷,提高效率。如果類A中的函式要訪問類B中的成員(例如:智慧指標類的實現),那麼類A中該函式要是類B的友元函式。具體來說:為了使其他類的成員函式直接訪問該類的私有變數。即:允許外面的類或函式去訪問類的私有變數和保護變數,從而使兩個類共享同一函式。

實際上具體大概有下面兩種情況需要使用友元函式:

(1)運算子過載的某些場合需要使用友元。

(2)兩個類要共享資料的時候。

1.1使用友元函式的優缺點

優點:能夠提高效率,表達簡單、清晰。

缺點:友元函式破環了封裝機制,儘量不使用成員函式,除非不得已的情況下才使用友元函式。

1.2友元函式的引數

因為友元函式沒有this指標,則引數要有三種情況:

(1)要訪問非static成員時,需要物件做引數;

(2)要訪問static成員或全域性變數時,則不需要物件做引數;

(3)如果做引數的物件是全域性物件,則不需要物件做引數;

1.3友元函式的位置

因為友元函式是類外的函式,所以它的宣告可以放在類的私有段或公有段且沒有區別。

1.4友元函式的呼叫

可以直接呼叫友元函式,不需要通過物件或指標

友元函式和類的成員函式的區別

成員函式有this指標,而友元函式沒有this指標。

友元函式是不能被繼承的,就像父親的朋友未必是兒子的朋友。