一 :
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;
}