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

文章目录

  • 一、构建智能指针的原因
  • 二、智能指针分析
  • 三、实现智能指针
    • 数据结构使用的编译器
      • 创建Qt控制台项目
    • 智能指针的构建
      • 新文件结构
      • 创建指针成员
      • 实现析构函数
      • 构造函数
      • 操作符重载
      • 成员函数的实现
      • 拷贝构造函数和"="重载操作符
  • 四、代码一览
  • 总结


一、构建智能指针的原因

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

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

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

二、智能指针分析

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

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

三、实现智能指针

数据结构使用的编译器

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

创建Qt控制台项目

1、在侧边栏打开qt creator
【C++数据结构】智能指针的构建_第1张图片
如果在侧边栏未找到怎么办?
那就进行下列操作:
【C++数据结构】智能指针的构建_第2张图片在侧边栏找到点的按钮,点击他。
【C++数据结构】智能指针的构建_第3张图片
在上方搜索qt,下面的这个应用就是qt,点击打开他。

2、创建新的项目
【C++数据结构】智能指针的构建_第4张图片
进入之后点击New Project

【C++数据结构】智能指针的构建_第5张图片
之后选择第二个。
之后一路Choose即可,注意在此步的下一步是选择路径的,路径不能有中文

进入之后就是这样的:
【C++数据结构】智能指针的构建_第6张图片

智能指针的构建

点击我们的项目,创建新的文件:
【C++数据结构】智能指针的构建_第7张图片
【C++数据结构】智能指针的构建_第8张图片
选择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;
}

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

【C++数据结构】智能指针的构建_第9张图片
具体的过程如图所示

拷贝构造函数:

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中,最重要的就是要实现析构函数的自动释放功能

你可能感兴趣的:(C++数据结构,c++,数据结构,数据库,c语言,开发语言,软件工程,算法)