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

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

时间: 2024-10-16 01:34:56

c++单链表【构造函数、运算符重载、析构函数、增删查改等】的相关文章

c++双链表【构造函数、运算符重载、析构函数、增删查改及逆置等】

c++中的双向链表写法,主要实现(增删查改,链表逆置,构造函数,运算符重载,等) 建立头文件SList.h #pragma once typedef int DataType; class ListNode { friend class List;//友元函数 public: ListNode(const DataType x) :_data(x) , _prev(NULL) , _next(NULL) {} private: DataType _data; ListNode* _prev; L

c++中的顺序表写法,主要实现(增删查改,构造函数,运算符重载)

本文的内容主要是,利用c++写出顺序表,并对写出的代码进行测试, 主要实现的功能:实现对顺序表的增删查改, 要写的函数:构造函数,赋值运算符重载,析构函数.在编写代码过程中应注意到深浅拷贝问题. 下面是顺序表的类函数: #pragma once #include<iostream> using namespace std; typedef int DataType; class SeqList { public: SeqList(); SeqList(DataType *array, size

单链表的增删查改等基本操作C++实现

单链表的初始化.增删查改.遍历一次找中间结点.删除一个无头单链表的非尾结点(不给头结点) #include<stdio.h> #include<stdlib.h> #include<malloc.h> typedef int DataType; typedef struct ListNode { struct ListNode* _next; DataType _data; }ListNode; void InitList(ListNode* &pHead) {

string类的增删查改实现

string类的默认成员函数.增删查改实现 #include<iostream> #include<assert.h> using namespace std; class String { public: String(char* _str="") //:p_str((char*)malloc(strlen(_str)+1))             //效果一样,但之前没考虑清楚,误打误撞对了,没注意,开辟空间应于_capacity相等 { cout<

mysql基础知识之增删查改使用介绍

 mysql基础知识之增删查改使用介绍 本文主要介绍mysql常用的SELECT.INSERT.UPDATE.DELETE语句的使用,数据库的安装这里不做介绍,并且事先已经准备好相关数据. 本文中使用的student表中的数据如下: mysql> SELECT * FROM student; 一.SELECT语句的使用 1.单表查询 语法格式: SELECT culom1,culom2,culom3 FROM TABLE_NAME; 可以改变字段显示顺序 2.WHERE子句查询 语法格式: SE

在MVC程序中,使用泛型仓储模式和工作单元实现增删查改

在这片文章中,我将自己动手为所有的实体:写一个泛型仓储类,还有一个工作单元. 工作单元的职责就是:为每一个实体,创建仓储实例.仓储(仓库)的职责:增删查改的功能实现. 我们将会在控制器中,创建工作单元类(UnitOfWork)的实例,然后根据实体,创建仓储实例,再就是使用仓储里面的方法,做操作了. 下面的图中,解释了,仓储和EF 数据上文的关系,在这个图里面,MVC控制器和仓储之间的交互,是通过工作单元来进行的,而不是直接和EF接触. 那么你可能就要问了,为什么要使用工作单元??? 工作单元,就

asp.net MVC最简单的增删查改!(详)

折腾了两天搞出来,但原理性的东西还不是很懂,废话不多说上图上代码 然后右键models,新建一个数据模型 注意我添加命名为lianxi 添加后如上 接下来在controllers添加控制器还有在Views中添加视图 注意控制器lianxi和视图的名字要一致,然后视图我是添加了3个分别是Index,insert,Modify,在控制器里分别有三个对应的函数 每当用URL访问视图时,他就调用了controllers对应的方法,例如 jiaEntities 就是建立模式时那个数据链接的名字      

实现基本的增删查改功能

1. In the previous tutorial you created an MVC application that stores and displays data using the Entity Framework and SQL Server LocalDB. In this tutorial you'll review and customize the CRUD (create, read, update, delete) code that the MVC scaffol

Asp.Net+Oracle+EasyUI简单增删查改

Asp.Net+Oracle+EasyUI简单增删查改 概要:网上有很多关于EasyUI前端框架的资料,本人在学习的基础上,基于自己之前搭建的框架(Oracle+Ado.Net),配合EasyUI实现一套简单的增删查改. 正文: 在实体层新建一个EMP.cs,继承至BaseModel 1 namespace myOracle.Model 2 { 3 public class Emp:BaseModel 4 { 5 public Emp() 6 { 7 base.PrimaryKey = "emp