模板实现顺序表

本文探讨了在模板实现顺序表时,如何通过for循环进行深拷贝,以避免浅拷贝可能导致的问题,特别是针对T为string类型的情况。
摘要由CSDN通过智能技术生成

一   :

SeqList.h

#pragma once
#include<iostream>
#include<assert.h>
#include <string>
using namespace std;
template <class T>
class SeqList
{

public:
	SeqList()
		:_a(NULL)
		, _size(0)
		, _capacity(0)
	{}
	~SeqList()
	{
		if (_a)
		{
			free(_a);
		}
	}
	void PushBack(const T&x)//最好传引用,不传引用,如果T为string,传参时拷贝构造(深拷贝,至少引用计数的浅拷贝)代价太大
	{
		CheckCapacity();
		_a[_size] = x;
		_size++;
	}
	void CheckCapacity()
	{
		if (_size >= _capacity)
		{
			_capacity = _capacity > 0 ? _capacity * 2 : 3;
			_a = (T*)realloc(_a, _capacity*sizeof(T));//切记,realloc malloc calloc申请的单位都是字节
			assert(_a);
		}
	}
	void Print()
	{
		for (size_t i = 0; i < _size; i++)
		{
			cout << _a[i] << " ";
		}
		cout << endl;
	}
private:
	T* _a;
	size_t _size;
	size_t _capacity;
};

test.cpp
#include"SeqList.h"
void  TestSeqList()
{
	/*SeqList <int> s1;
	s1.PushBack(1);
	s1.PushBack(2);
	s1.PushBack(3);
	s1.PushBack(4);
	s1.Print();*/
	SeqList <string>s3;
	s3.PushBack("111");
	s3.PushBack("222");
	s3.PushBack("444");
	s3.PushBack("555");
	s3.Print();
};
int main()
{
	TestSeqList();
	system("pause");
	return 0;
}

以上代码会出现问题,当T是int时不出错,当T为string时程序崩溃

什么原因造成的呢?

调用发现出错在operator =  ,通过调用堆栈调试发现出错再CheckCapacity() ,_a=(T*)realloc(_a,_capacity*sizeof(int))提示为错误的指针。
错误原因是:realloc和new的区别,new会调用构造函数,realloc不会调用构造函数 ,对象没有初始话,_a指向随机值,赋值时会出错,operator=时,是深拷贝会先释放原来空间,释放随机值因此出错。
如何改正呢?
二 :
那我们就用new来申请空间,delete来析构
将析构函数改为:
~SeqList()
	{
		if (_a)
		{
			delete[]_a;
		}
	}

将CheckCapacity()改为:

void CheckCapacity()
	{
		if (_size >= _capacity)
		{
			_capacity = _capacity > 0 ? _capacity * 2 : 3;
			//_a = (T*)realloc(_a, _capacity*sizeof(T));//切记,realloc malloc calloc申请的单位都是字节
			T * new_a = new T[_capacity];
			if (_a)
			{
				memcpy(new_a, _a, sizeof(T)*_size);
			}
			_a = new_a;
		}
	}

可是上面用memcpy的这种情况当T为string时也会出现(浅拷贝)的问题。
三  :

接下来我们先看一下string的相关问题


因此:我们就可以知道,如果第一次存放的数据>=16,就会动态开辟,用ptr指向。当内存不够时,CheckCapacity会动态开辟内存,memcpy是按字节拷贝,它把ptr指针拷贝下来(浅拷贝),拷贝完会把之前的析构调,因此新的ptr相当一个野指针,指向随机值,相当于两个ptr指向一块空间,析构时会析构两次会出错。

四  :
该怎么解决呢?

用for循环赋值运算,因为我们知道string赋值运算符重载它是深拷贝,因此将CheckCapacity()改为


void CheckCapacity()
	{
		
		if (_size >= _capacity)
		{
			_capacity = _capacity > 0 ? _capacity * 2 : 3;
			T *new_a = new T[_capacity];
			if (new_a)
			{
				for (size_t i = 0; i < _size; i++)
				{
					new_a[i] = _a[i];
				}
			}
			delete[]_a;
			_a = new_a;
		}
	}

模板实现顺序表注意:
1.开空间用new,析构空间用delete.因为new开的空间会调用构造函数,会初始化。

2.拷贝数据用for循环拷贝(memcpy 浅拷贝,如果T为string会出错) for循环拷贝,为赋值,而赋值运算符重载为深拷贝。

四   完整版,主要是实现拷贝构造,构造,赋值运算符重载,析构等等  及接口就只实现了一个,因为前面已经实现很多次了
SeqList.h
#pragma once
#include<iostream>
#include<assert.h>
#include <string>
using namespace std;
template <class T>
class SeqList
{

public:
	SeqList()
		:_a(NULL)
		, _size(0)
		, _capacity(0)
	{}
	SeqList(const T*a, size_t size,size_t capacity)//带参构造
		:_a(new T[capacity])
		,_size(size)
		, _capacity(capacity)

	{
		assert(a);
		for (size_t i = 0; i < size; i++)
		{
			_a[i] = a[i];
		}
	}

	~SeqList()
	{
		if (_a)
		{
			delete[]_a;
		}
	}
	SeqList(const  SeqList <T>& s)//拷贝构造  借助构造函数   现代写法
	{
		SeqList<T> tmp(s._a,s._size,s._capacity);
		swap(_a, tmp._a);
		swap(_size, tmp._size);
		swap(_capacity, tmp._size);

	}
	//SeqList(const SeqList <T>&s)//拷贝构造    自己实现    传统写法
	//{
	//	_a = new T[s._size];
	//	for (size_t i = 0; i < s._size; i++)
	//	{
	//		_a[i] = s._a[i];
	//	}
	//	_size = s._size;
	//	_capacity = s._capacity;
	//}
	/*SeqList<T> &operator=(SeqList <T> s)//赋值运算符重载   也是借用拷贝构造   现代写法   注意此处没有引用
	{
		if (this != &s)
		{
			swap(_a, s._a);
			swap(_size, s._size);
			swap(_capacity, s._capacity);
		}
		return *this
	}*/
	//SeqList<T> &operator=(SeqList <T>& s)//赋值运算符重载   现代写法
	//{
	//	if (this != &s)
	//	{
	//		SeqList <T> tmp(s);//借助拷贝构造
	//		swap(tmp._a, _a);
	//		swap(tmp._size, _size);
	//		swap(tmp._capacity, _capacity);
	//	}
	//	return *this
	//}
	SeqList <T>&operator=(SeqList <T>& s)//赋值运算符重载  传统写法
	{
		_size = s._size;
		_capacity = s._capacity; 
		_a = new T [s._capacity];
		for (size_t i = 0; i < _size; i++)
		{
			_a[i] = s._a[i];
		}
	}

	void PushBack(const T&x)//最好传引用,不传引用,如果T为string,传参时拷贝构造(深拷贝,至少引用计数的浅拷贝)代价太大
	{
		CheckCapacity();
		_a[_size] = x;
		_size++;
	}
	//void CheckCapacity()
	//{
	//	if (_size >= _capacity)
	//	{
	//		_capacity = _capacity > 0 ? _capacity * 2 : 3;
	//		_a = (T*)realloc(_a, _capacity*sizeof(T));//切记,realloc malloc calloc申请的单位都是字节
	//		T * new_a = new T[_capacity];
	//		if (new_a)
	//		{
	//			memcpy(new_a, _a, sizeof(T)*_size);//如果T为string,用这个可能会出错
	//		}
	//		_a = new_a;
	//	}
	//}
	void CheckCapacity()
	{
		
		if (_size >= _capacity)
		{
			_capacity = _capacity > 0 ? _capacity * 2 : 3;
			T *new_a = new T[_capacity];
			if (new_a)
			{
				for (size_t i = 0; i < _size; i++)
				{
					new_a[i] = _a[i];
				}
			}
			delete[]_a;
			_a = new_a;
		}
	}
	void Print()
	{
		for (size_t i = 0; i < _size; i++)
		{
			cout << _a[i] << " ";
		}
		cout << endl;
	}
private:
	T* _a;
	size_t _size;
	size_t _capacity;
};

test.c

#include"SeqList.h"
void  TestSeqList()
{
	/*SeqList <int> s1;
	s1.PushBack(1);
	s1.PushBack(2);
	s1.PushBack(3);
	s1.PushBack(4);
	s1.Print();*/
	SeqList <string>s3;
	s3.PushBack("1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111");
	s3.PushBack("222");
	s3.PushBack("444"); 
	s3.PushBack("555");
	s3.PushBack("666");
	s3.Print();
	SeqList <string> s2(s3);
	s2.Print();
};
int main()
{
	TestSeqList();
	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值