【C++数据结构】智能指针的构建


一、构建智能指针的原因

众所周知,C++是没有垃圾回收的,就会导致以下问题:
1、导致动态申请堆空间,用完后不归还
2、会导致程序的内存泄露,进而影响整个程序,甚至可能是整个电脑
3、指针无法控制所指堆空间的生命周期

我们就需要设计一个类SmartPointer他的功能如下:

  • 指针生命周期结束时主动释放堆空间
  • 一片堆空间最多由一个指针标识
    原因:因为我们要在析构函数中释放堆空间,如果2个智能指向同一片空间,就会导致他被释放2次,就会得到意想不到的结果!!!
  • 杜绝指针运算和指针比较。

二、智能指针分析

  1. 通过类模板描述指针的行为。
    在之前的第一节课我们已经讲过了,可以使用类模板来加强我们的数据结构的复用,在这里我们也是使用类模板来定义我们的SmartPointer使他能够定义不同类型的指针对象

  2. 重载指针特征操作符(->*)。
    利用对象模拟原生指针的行为

三、实现智能指针

数据结构使用的编译器

我使用的编译器是Qt5.9.4,操作系统环境为Ubuntu 22,如果大家使用的是vs/qt(其他版本),或是其他操作系统,我都会讲他们函数的不同地方,例如函数名称的不同!!!

创建Qt控制台项目

1、在侧边栏打开qt creator
在这里插入图片描述
如果在侧边栏未找到怎么办?
那就进行下列操作:
在这里插入图片描述在侧边栏找到点的按钮,点击他。
在这里插入图片描述
在上方搜索qt,下面的这个应用就是qt,点击打开他。

2、创建新的项目
在这里插入图片描述
进入之后点击New Project

在这里插入图片描述
之后选择第二个。
之后一路Choose即可,注意在此步的下一步是选择路径的,路径不能有中文

进入之后就是这样的:
在这里插入图片描述

智能指针的构建

点击我们的项目,创建新的文件:
在这里插入图片描述
在这里插入图片描述
选择C++ Header File,因为我们要使用模板技术,所以只需要头文件即可。具体的原因如下:
使用C++模板技术时,只需要创建头文件的原因是因为模板是一种在**编译时生成代码的机制**。在C++中,模板可以用来定义通用的数据结构和算法,以适应不同类型的数据
当我们编写一个模板类或者模板函数时,我们只需要把模板的声明和定义写在头文件中即可。这是因为模板的实例化是在编译时完成的,而不是链接或运行时。当程序在编译时遇到模板的使用,编译器会根据实际使用的类型生成对应的代码。
由于模板的代码是泛化的,可以适用于不同的类型,在编译时需要进行模板的实例化。如果把模板定义分离到实现文件中,编译器无法在编译时生成相应的实例化代码,导致链接时出现编译错误。
因此,为了正确使用C++模板技术,我们通常将模板的声明和定义都写在头文件中。这样,在包含头文件时,编译器可以看到模板的定义,并根据需要生成相应的实例化代码,保证程序的正确性。
需要注意的是,模板的成员函数的定义通常也需要放在头文件中,以便在实例化时能够正确地内联函数。如果将模板的定义和实现都放在头文件中,会使代码更易于 维护和理解,并且避免了编译和链接时的错误。


在我们本数据结构,我们都会使用一个叫做命名空间来包含我们的数据结构类
他的语法格式如下:

namespace MYLib
{
	//someclass
	class Myclass
	{
		
	};
	//someFunction
	void func()
	{
		
	}
}

我们在使用他的时候就需要uising namespace MYLib和添加对应的头文件,才可以使用类,要不然需要像这样MYLib::func()来使用

新文件结构

#ifndef SMARTPOINTER_H
#define SMARTPOINTER_H

namespace MyStruct
{
    //your class
}

#endif // SMARTPOINTER_H

创建指针成员

我们在类中需要使用原生指针。并且我们还需要使用泛指类型

	template<typename T>
    class SmartPointer
    {
    protected:
        T *m_pointer;
    public:
        
    };

实现析构函数

在我们智能指针中,最重要的就是自动释放指针,所以我们要使用析构函数来帮我们释放指针。

~SmartPointer()
{
    delete m_pointer;
}

构造函数

只需要把m_pointer指向参数即可,参数需要默认参数提法灵活度

SmartPointer(T*point = nullptr)
{
    m_pointer = point;
}

操作符重载

*操作符分析

int *a = new int(10);
cout << *a << endl;

通过运行上面的程序,我们可以发现*他是一个取值操作符,所以我们重载他的时候需要返回一个具体的数,而不是指针
具体实现如下:

T& operator *()
{
     return *m_pointer;
}

->操作符分析:

T*operator ->()
{
    return m_pointer;
}

成员函数的实现

1、判断是否为空

bool isNull()
{
    return m_pointer == nullptr;
}

2、得到指针

T *get()
{
    return m_pointer;
}

拷贝构造函数和"="重载操作符

在这里插入图片描述
具体的过程如图所示

拷贝构造函数:

SmartPointer(const SmartPointer&obj)
{
    m_pointer = obj.m_pointer;//本类的指针指向obj里面的指针指向的东西
	
	/*使用const_cast消除const属性*/
    const_cast<SmartPointer<T>>(obj).m_pointer = nullptr;//把参数的m_pointer指向null
}

"="重载:
此处实现和上面差不多,无非是需要判断是否为自赋值
自赋值是什么:

int a = 10;
a = a;

像上面这种a =a;就属于自赋值,此时我们如果去做一遍赋值操作就会大大降低效率所以需要避免!!!

判断自赋值的方法如下:
通过比较地址即可知道是否为同一个类的自赋值

SmartPointer<T> &operator =(const SmartPointer&obj)
{
    if(&obj!=this)
    {
        m_pointer = obj.m_pointer;

        const_cast<SmartPointer<T>>(obj).m_pointer = nullptr;
    }
    
    return *this;//放回自身,加强连续赋值
}

四、代码一览

#ifndef SMARTPOINTER_H
#define SMARTPOINTER_H

namespace MyStruct
{

    template<typename T>
    class SmartPointer
    {
    protected:
        T *m_pointer;
    public:
        SmartPointer(T*point = nullptr)
        {
            m_pointer = point;
        }

        SmartPointer(const SmartPointer&obj)
        {
            m_pointer = obj.m_pointer;

            const_cast<SmartPointer<T>>(obj).m_pointer = nullptr;
        }
        
        SmartPointer<T> &operator =(const SmartPointer&obj)
        {
            if(&obj!=this)
            {
                m_pointer = obj.m_pointer;
    
                const_cast<SmartPointer<T>>(obj).m_pointer = nullptr;
            }
            
            return *this;
        }

        T& operator *()
        {
            return *m_pointer;
        }

        T*operator ->()
        {
            return m_pointer;
        }

        bool isNull()
        {
            return m_pointer == nullptr;
        }

        T *get()
        {
            return m_pointer;
        }

        ~SmartPointer()
        {
            delete m_pointer;
        }
    };

}

#endif // SMARTPOINTER_H

总结

SmartPointer中,最重要的就是要实现析构函数的自动释放功能

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人才程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值