函数模板在c++动态顺序表中的大作用

函数模板提供了一种机制通过它我们可以保留函数定义和函数调用的语义在一个程序位置上封装了一段代码确保在函数调用之前实参只被计算一次.

函数模板提供一个种用来自动生成各种类型函数实例的算法程序员对于函数接口参数和返回类型中的全部或者部分类型进行参数化(parameterize)而函数体保持不变.

函数模板使用注意事项:

1、每个函数模板前边都需要添加模板声明例如:template<typename T>

2、在模板类的使用时,注意其类型改变,例如:

template<typename T>
class SeqList
{};
该模板类的类型为SeqList<T> ,SeqList仅为一个类名

3、函数模板不能在.h文件和.cpp文件中分开编译

在实现顺序表的时候我们需要其可以存储任何类型的数据。但是对于string类型对象要特别考虑。下面我们来分析一下常用的拷贝的方法。

1)逐个对象的复制

优点:对于数据的类型无需特别考虑

缺点:拷贝的效率比较低

2)memcpy()复制

优点:拷贝的效率比较高

缺点:在涉及指针的复制时,可能出现多个指针指向同一份空间,导致内存释放多次引起程序崩溃。

string类深度剖析

在不同的编译器中string类的空间大小不同,但是可以确定的是,string类存在一个16个字节大小的buffer空间用于存放长度较短的字符来提高效率,还有一个4字节的——ptr用于指向当字符长度大于15时的字符串空间,还有两个4字节的_size和_capacity用来表示大小和容量。总共28个字节。

这里就需要考虑在扩容和拷贝构造时的复制了,如果字符串的长度大于15个,则使用memcpy()仅仅是数值的拷贝,并不会为_ptr申请空间会导致多个指针同时指向同一份空间,使得该空间在释放时释放多次导致程序崩溃。

#include <iostream>
#include<string>
using namespace std;

const int SIZE = 3;//构造对象的默认大小
const int DEAFAULT = 3;//每次扩容的大小
template<typename T>
class SeqList
{
public:
	//构造函数
	SeqList();
	//拷贝构造函数
	SeqList(const SeqList& l);
	//赋值
	SeqList<T> operator=(SeqList l);
	//析构函数
	~SeqList();
public:
	void PushBack(const T& d);
	void PopBack();
	void PushFront(const T& d);
	void PopFront();
	void SortList();
	void Reverse();
	T* Find(const T& d);
	void Insert(int pos, const T& x);
	void Remove(T x);
	void RemoveAll(T x);
	void CheckCapacity();
	void Print();
private:
	T* _data;
	int _size;
	int _capacity;

};
template<typename T>
void SeqList<T>::Print()
{
	int i = 0;
	for (i = 0; i < _size; i++)
	{
		cout << _data[i] << " ";
	}
	cout << endl;
}
template<typename T>
//构造函数
SeqList<T>::SeqList()
	:_data(new T[SIZE])
	, _size(0)
	, _capacity(SIZE)
{}
template<typename T>
//拷贝构造函数
SeqList<T>::SeqList(const SeqList& l)
{
	_data = new T[l._size];
	int i = 0;
	for (i = 0; i < l._size; i++)//逐个拷贝
	{
		_data[i] = l._data[i];
	}
	_size = l._size;
	_capacity = _size;
}
template<typename T>
//赋值
SeqList<T> SeqList<T>:: operator=(SeqList l)
{
	swap(_data, l._data);
	_size = l._size;
	_capacity = l._capacity;
	return *this;
}
template<typename T>
//析构函数
SeqList<T>::~SeqList()
{
	if (_data != NULL)
	{
		delete[] _data;
		_data = NULL;
	}
}

template<typename T>
void SeqList<T>::CheckCapacity()//检测容量
{
	if (_size == _capacity)//扩容
	{
		T* tmp = new T[_capacity + DEAFAULT];
		//memcpy(tmp, _data, sizeof(T)*_size);浅拷贝,导致程序崩溃
		int i = 0;
		for (i = 0; i < _size; i++)//逐个拷贝
		{
			tmp[i] = _data[i];
		}
		delete[] _data;
		_data = tmp;
		_capacity += DEAFAULT;
	}
}
template<typename T>
void SeqList<T>::PushBack(const T& d)
{
	CheckCapacity();
	_data[_size] = d;
	_size++;
}
template<typename T>
void SeqList<T>::PopBack()
{
	if (_size == 0)
	{
		return;
	}
	_size--;
}
template<typename T>
void SeqList<T>::PushFront(const T& d)
{
	CheckCapacity();
	int i = 0;
	for (i = _size-1; i >= 0; i--)
	{
		_data[i + 1] = _data[i];
	}
	_data[0] = d;
	_size++;
}
template<typename T>
void SeqList<T>::PopFront()
{
	if (_size == 0)
	{
		return;
	}
	int i = 0;
	for (i = 1; i < _size; i++)
	{
		_data[i - 1] = _data[i];
	}
	_size--;
}
template<typename T>
void SeqList<T>::SortList()
{
	int i = _size;
	bool flag = true;//标志位,当顺序表已经是有序的时候则不需要再排序提高效率
	for (i = 1; i < _size && flag; i++)
	{
		int j = 0;
		flag = false;
		for (j = 0; j < _size - i; j++)
		{
			if (_data[j] > _data[j + 1])
			{
				flag = true;
				swap(_data[j], _data[j + 1]);
			}
		}
	}
}
template<typename T>
void SeqList<T>::Reverse()
{
	int left = 0;
	int right = _size;
	while (left < right)
	{
		swap(_data[left++], _data[right++]);
	}
}
template<typename T>
T* SeqList<T>::Find(const T& d)
{
	int i = 0;
	for (i = 0; i < _size; i++)
	{
		if (_data[i] == d)
		{
			return &_data[i];//找到返回地址
		}
	}
	return NULL;//没有找到
}
template<typename T>
void SeqList<T>::Insert(int pos, const T& x)
{
	if (pos > 0 && pos <= _size)//判断位置的正确性
	{
		CheckCapacity();
		int i = 0;
		for (i = _size - 1; i >pos-2; i--)
		{
			_data[i + 1] = _data[i];
		}
		_data[pos - 1] = x;
		_size++;
	}
}
template<typename T>
void SeqList<T>::Remove(T x)
{
	int i = 0;
	for (i = 0; i < _size; i++)
	{
		if (x == _data[i])
		{
			for (int j = i; j < _size-1; j++)
			{
				_data[j] = _data[j + 1];
			}
			_size--;
			return;
		}
	}
}
template<typename T>
void SeqList<T>::RemoveAll(T x)
{
	int i = 0;
	for (i = 0; i < _size; i++)
	{
		if (x == _data[i])
		{
			Remove(x);
			i--;//保留下标,删除之后从当前继续寻找,以免顺序表遍历漏掉。
		}
	}
}

void test1()
{
	SeqList<string> l;
	//l.PushBack(1);
	//l.PushBack(2);
	//l.PushBack(3);
	//l.PushBack(4);
	//l.Print();
	//l.PopBack();
	//l.Print();
	//l.PopBack();
	//l.Print();
	//l.PopBack();
	//l.Print();
	l.PushFront("11111");
	l.PushFront("22222");
	//l.PushFront("333333333333333333333333333333333333");
	l.PushFront("33333");
    l.PushFront("44444");
	//l.PushFront("11111");
	l.Print();
	cout << sizeof(string) << endl;
	//cout << l.Find("11111") << endl;
	////cout << l.Find(0) << endl;
	//l.Insert(4, "00000");
	//l.Print();
	//l.RemoveAll("11111");
	//l.Print();
	//l.SortList();
	////l.PopBack();
	//l.Print();
	//l.PopBack();
	//l.Print();
	//l.PopBack();
	//l.Print();
}
int main()
{
	test1();
	getchar();
	return 0;
}

当使用memcpy()时

测试代码

        l.PushFront("11111");
	l.PushFront("22222");
	//l.PushFront("333333333333333333333333333333333333");
	l.PushFront("33333");
    l.PushFront("44444");

结果可以正常运行

但是在字符串长度比较长时

        l.PushFront("11111");
	l.PushFront("22222");
	l.PushFront("333333333333333333333333333333333333");
	//l.PushFront("33333");
    l.PushFront("44444");

程序崩溃

时间: 2024-11-02 01:51:47

函数模板在c++动态顺序表中的大作用的相关文章

c实现的动态顺序表

第一篇文章中用c实现了静态顺序表,但是使用静态顺序表还有不足的地方.当我们需要存储的数据很少时,如果静态顺序表的数组容量较大就会造成空间的浪费:当我们需要存储的数据很多时,如果静态顺序表的数组容量较小可能就会造成数据丢失.所以一般情况我们应该尽量把顺序表实现成动态的.需要多大容量就开辟多大容量. 静态顺序表和动态顺序表只有以下函数不同: 1.定义结构体时,多定义一个capacity,并对capacity进行初始化和增加大小的设置: #define INIT_CAPACITY 3 #define 

动态顺序表 与 双向链表的模板类

//////////////////////////////////////////////////////////////////////// /////////////////////泛型编程之动态顺序表的模板///////////////////////// //////////////////////////////////////////////////////////////////////// #include<iostream> #include<string> u

C++实现动态顺序表

顺序表是在计算机内存中以数组的形式保存的线性表,是指用一组地址连续的存储单元依次存储数据元素的线性结构.这样的存储方式使得线性表逻辑上相邻的元素,其在物理存储单元中也是相邻的.只要知道了第一个元素的存储地址,就可以知道线性表中任何一个元素的存储地址.本文利用C++语言,在Windows平台 Visual Studio 2015开发环境下实现.功能:应用C++语言实现顺序表的各项操作.基本的成员函数:构造函数.拷贝构造函数.赋值运算符的重载.析构函数. // 顺序表构造成功之后,里面存放了n个元素

静态顺序表和动态顺序表

实现一个静态顺序表,首先,要定义一个保存数据的数组,保存在结构体中,用size来存储数组中的元素个数, typedef struct SeqList { DataType array[MAX_SIZE]; size_t size; }SeqList; 首先来实现一下静态顺序表的初始化函数,可以借用系统的memset函数来实现,开辟一块空间全部初始化为0,没有存入数据所以size也为0 void InitSeqList(SeqList *pSeq) { assert(pSeq); memset(p

【C语言】静态顺序表和动态顺序表的实现

静态顺序表 定义一张顺序表也就是在内存中开辟一段连续的存储空间,并给它一个名字进行标识.只有定义了一个顺序表,才能利用该顺序表存放数据元素,也才能对该顺序表进行各种操作. 有两种定义顺序表的方法:一是静态地定义一张顺序表:二是动态地生成一张顺序表. 静态地定义一张顺序表的方法与定义一个数组的方法类似.可以描述如下: #define MAX_SIZE 100 typedef int DataType; typedef struct SeqList { DataType array[MAX_SIZE

动态顺序表(可分配内存空间)

<strong style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">前几天写了一个静态顺序表,但是觉得一开始开辟很大一部分空间却不一定可以完全用得上,会造成很大的内存浪费,于是写了一个可以自动开辟内存空间的动态顺序表作为改进.</strong> "DynamicSeqllist.h" #pragma once #def

C语言:【动态顺序表】动态顺序表的初始化、打印、尾插PushBack、尾删PopBack

#include<stdio.h> #include<stdlib.h> #include<assert.h> #include<string.h> #include<malloc.h> typedef int DateType; typedef struct SeqList {     DateType *arr;     size_t capacility;     size_t size; }SeqList; //创建空间 void Che

_DataStructure_C_Impl:在顺序表中查找元素

// _DataStructure_C_Impl:Search #include<stdio.h> #include<stdlib.h> #define MaxSize 100 #define IndexSize 20 typedef int KeyType; //元素的定义 typedef struct{ KeyType key; }DataType; //顺序表的类型定义 typedef struct{ DataType list[MaxSize]; int length; }

顺序表中基本操作

前言:最近玩esp8266和ucos-iii以及在学c++比较多,接触的大部分都是指针.结构体.链表:刚好自己又在看数据结构(数据结构真的非常重要,要好好学,是学算法的敲门砖,哈哈哈),个人看法在对顺序表进行元素增和删操作,效率比较低(要移动非常多的其他元素),而我之前写的对链表操作,使用指针操作,效率就高多了.好了,来看今天的学习总结吧! 一.顺序表的初始化: 算法步骤: 1.为顺序表L动态分配一个预定义大小的数组空间,使elem指向这段空间的基地址 2.将表的当前长度设为0 伪代码实现: S