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指针。

友元函数是不能被继承的,就像父亲的朋友未必是儿子的朋友。

你可能感兴趣的:(单链表,构造函数,友元函数)