众所周知,一块连续(至少逻辑上连续)的物理地址可以有多个指针指向(或者说多个引用在引用)它,而我们想要最简单的效果是:当没有指针指向该内存时,该内存就应该被释放,即指向该内存的最后一个指针消失后,该内存就应该被回收。想要实现这一功能,就必须有一个与该连续的物理地址相关联的变量记录着该物理地址被指针指向的数量。
在此之前,我们先来看一下该模板类简单的定义,如下:
template<typename T>
class Reference{
private:
int *Num_of_Ref; //指向存放指向数据内存的引用个数变量的指针
int Size;
T *P_Data; //指向数据的指针
public:
Reference(int sz = 0); //构造函数,分配sz个T类数据大小的内存
Reference(T*, int);
Reference(const Reference<T>&); //复制构造函数
~Reference(); //析构函数
void build(int); //建立一个数组
void destoy(); //摧毁一个数组
void memory_to(int); //将内存扩展到(用该函数进行内存的增加与缩小)
operator int(){ return int(P_Data); } //查看地址值
const Reference<T>& operator=(const Reference<T>&); //赋值函数
T& operator[](int i){ return P_Data[i]; } //索引访问函数
bool operator==(const Reference<T>&)const; //相等比较函数
const Reference<T>& Reference_to(const Reference<T>&); //引用赋予函数,相当于地址赋值
int size()const{ return Size; } //内存数量(以数据类的大小为单元)
};
综上所述,管理动态内存,最简单的,就必须要有一个与所分配的动态内存相关联的一个计数的变量,最简单的,该计数变量再动态内存分配时也是由动态内存分配;如下:
template <class T>
void Reference<T>::build(int sz) //建立一个数组
{
Size = sz; //大小,默认为0
if (sz == 0) //如果想要创建的输入为0
{
P_Data = nullptr; //指针指空,而后结束
Num_of_Ref = nullptr;
return;
}
P_Data = new T[Size]; //分配用于储存数据的动态内存
Num_of_Ref = new int[1]; //分配用于储存指向数据内存指针数量的变量内存
Num_of_Ref[0] = 1; //此时指向该内存的指针数为1
}
代码中P_Data即是指向数据的指针,Num_of_Ref则是指向记录指向数据内存指针个数的变量的指针。再释放该内存时就需要先访问Num_of_Ref[0](此处使用数组而不是单一变量的原因是,该变量其实是与实际物理内存相关联的,相关联的可能不止这一个变量,可以扩展其它信息)查看当前有多少指针指向该内存,如果除了此时调用的这个类以外没有其它指针指向,就释放该内存,否则,只是计数减1,如下:
template <class T>
void Reference<T>::destoy() //析构一个数组
{
if (P_Data == nullptr) //如果该指针指空,则无需释放
return;
if (Num_of_Ref[0] == 1) //如果只有当前指针指向该块内存
{
delete[]P_Data; //释放无指针指向的内存
delete[]Num_of_Ref;
}
else
Num_of_Ref[0] -= 1; //指向该内存的个数减1
P_Data = nullptr; //释放之后自己指针指空
Num_of_Ref = nullptr;
Size = 0;
}
综上所述,当该变量被摧毁时,它会自己检查自己动态分配的内存是否有其它对象引用,如果有则不会释放内存,如果没有则会释放掉该内存,我们可以想象一下,如果我们平时所用的局部变量在该对象消失时,内存也会自己消失,同时如果作为返回值返回时就调用的复制构造函数,计数也会相应加1,然后自己被摧毁,但是此时内存并不会消失,至此我们就不用担心内存的问题,可以就像平时用基本类型那样使用它了。其复制构造函数如下:
template <class T>
Reference<T>::Reference(const Reference<T> & temp)
{
P_Data = nullptr; //指空
Num_of_Ref = nullptr;
Size = 0;
this->Reference_to(temp);
}
template <class T>
const Reference<T>& Reference<T>::Reference_to(const Reference<T> & temp) //数组之间的拷贝
{
if(temp.P_Data == nullptr)
return *this;
this->destoy(); //释放自己数据
Size = temp.Size; //个数赋值
P_Data = temp.P_Data;
Num_of_Ref = temp.Num_of_Ref; //指针赋值
Num_of_Ref[0] += 1; //指向该数据内存的指针(引用对象)数量加1
return *this;
}
此处要注意的是使用时不能如下使用:
Reference<int> example;
example = example(); //其中example()返回一个Reference<int>类型
因为这样会浪费内存,因为此处调用的赋值函数,其实现采用的是另外开辟一个空间然后把值赋值过去,并非是指针或者引用赋值。其具体实现如下:
template <class T>
const Reference<T> & Reference<T>::operator=(const Reference<T> & temp)
{
this->destoy(); //释放自己数据
this->build(temp.Size); //以目标对象内存大小创建新对象
for (int i = 0; i != Size; i++) //赋值
P_Data[i] = temp.P_Data[i];
return *this;
}
所以,正确的使用应该是:
Reference<int> example;
example.Reference_to(example()); //其中example()返回一个Reference<int>类型
另外,该类还提供了内存扩充或者减小的函数,如下:
template <class T>
void Reference<T>::memory_to(int num) //扩大或者缩小数组的内存到num
{
Reference<T> temp(*this); //创建一个临时对象拷贝之前数据
this->destoy(); //释放自己数据
this->build(num); //以所需内存大小创建新对象
int size = num;
if (num > temp.Size) //在之前内存个数与现在内存个数找出最小值
size = temp.Size;
for (int i = 0; i != size; i++) //将之前数据赋值到新的内存中
P_Data[i] = temp.P_Data[i];
}
该函数可以将内存扩大或者缩小到指定的大小。
最后,该类的完整实现如下:
#ifndef cass
#define cass
template<typename T>
class Reference{
private:
int *Num_of_Ref; //指向存放指向数据内存的引用个数变量的指针
int Size;
T *P_Data; //指向数据的指针
public:
Reference(int sz = 0); //构造函数,分配sz个T类数据大小的内存
Reference(T*, int);
Reference(const Reference<T>&); //复制构造函数
~Reference(); //析构函数
void build(int); //建立一个数组
void destoy(); //摧毁一个数组
void memory_to(int); //将内存扩展到(用该函数进行内存的增加与缩小)
operator int(){ return int(P_Data); } //查看地址值
const Reference<T>& operator=(const Reference<T>&); //赋值函数
T& operator[](int i){ return P_Data[i]; } //索引访问函数
bool operator==(const Reference<T>&)const; //相等比较函数
const Reference<T>& Reference_to(const Reference<T>&); //引用赋予函数,相当于地址赋值
int size()const{ return Size; } //内存数量(以数据类的大小为单元)
};
template <class T>
Reference<T>::Reference(int sz )
{
P_Data = nullptr; //指空
Num_of_Ref = nullptr;
Size = 0;
this->build(sz);
}
template <class T>
Reference<T>::Reference(T*pt, int num)
{
P_Data = nullptr; //指空
Num_of_Ref = nullptr;
Size = 0;
this->memory_to(num); //内存扩大到目标对象内存
for (int i = 0; i != num; i++) //赋值
P_Data[i] = pt[i];
}
template <class T>
Reference<T>::Reference(const Reference<T> & temp)
{
P_Data = nullptr; //指空
Num_of_Ref = nullptr;
Size = 0;
this->Reference_to(temp);
}
template <class T>
Reference<T>::~Reference()
{
this->destoy();
}
template <class T>
void Reference<T>::build(int sz) //建立一个数组
{
Size = sz; //大小,默认为0
if (sz == 0) //如果想要创建的输入为0
{
P_Data = nullptr; //指针指空,而后结束
Num_of_Ref = nullptr;
return;
}
P_Data = new T[Size]; //分配用于储存数据的动态内存
Num_of_Ref = new int[1]; //分配用于储存指向数据内存指针数量的变量内存
Num_of_Ref[0] = 1; //此时指向该内存的指针数为1
}
template <class T>
void Reference<T>::destoy() //析构一个数组
{
if (P_Data == nullptr) //如果该指针指空,则无需释放
return;
if (Num_of_Ref[0] == 1) //如果只有当前指针指向该块内存
{
delete[]P_Data; //释放无指针指向的内存
delete[]Num_of_Ref;
}
else
Num_of_Ref[0] -= 1; //指向该内存的个数减1
P_Data = nullptr; //释放之后自己指针指空
Num_of_Ref = nullptr;
Size = 0;
}
template <class T>
void Reference<T>::memory_to(int num) //扩大或者缩小数组的内存到num
{
Reference<T> temp(*this); //创建一个临时对象拷贝之前数据
this->destoy(); //释放自己数据
this->build(num); //以所需内存大小创建新对象
int size = num;
if (num > temp.Size) //在之前内存个数与现在内存个数找出最小值
size = temp.Size;
for (int i = 0; i != size; i++) //将之前数据赋值到新的内存中
P_Data[i] = temp.P_Data[i];
}
template <class T>
const Reference<T> & Reference<T>::operator=(const Reference<T> & temp)
{
this->destoy(); //释放自己数据
this->build(temp.Size); //以目标对象内存大小创建新对象
for (int i = 0; i != Size; i++) //赋值
P_Data[i] = temp.P_Data[i];
return *this;
}
template <class T>
bool Reference<T>::operator==(const Reference<T> & temp) const
{
if (Size != temp.Size) //判断个数是否相等
return false;
for (int i = 0; i != Size; i++) //判断数据是否相等
{
if (P_Data[i] != temp.P_Data[i])
return false;
}
return true;
}
template <class T>
const Reference<T>& Reference<T>::Reference_to(const Reference<T> & temp) //数组之间的拷贝
{
if(temp.P_Data == nullptr)
return *this;
this->destoy(); //释放自己数据
Size = temp.Size; //个数赋值
P_Data = temp.P_Data;
Num_of_Ref = temp.Num_of_Ref; //指针赋值
Num_of_Ref[0] += 1; //指向该数据内存的指针(引用对象)数量加1
return *this;
}
#endif
最后,关于它的一个应用,学生信息管理系统,我将会在另一篇文章详细介绍:
https://blog.csdn.net/yunengsheng/article/details/102154451