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

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

一、构建智能指针的原因


众所周知,C++是没有垃圾回收的,就会导致以下问题:

1、导致动态申请堆空间,用完后不归还

2、会导致程序的内存泄露,进而影响整个程序,甚至可能是整个电脑

3、指针无法控制所指堆空间的生命周期

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

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


二、智能指针分析


  1. 通过类模板描述指针的行为。
    在之前的第一节课我们已经讲过了,可以使用类模板来加强我们的数据结构的复用,在这里我们也是使用类模板来定义我们的SmartPointer使他能够定义不同类型的指针对象
  2. 重载指针特征操作符(->*)。
    利用对象模拟原生指针的行为


三、实现智能指针


数据结构使用的编译器

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


创建Qt控制台项目

1、在侧边栏打开qt creator

5b65084c21b64711832cba980d28bd7c.png

如果在侧边栏未找到怎么办?

那就进行下列操作:

6cef5886619541aba116b71c8bf2d1e5.png

在侧边栏找到点的按钮,点击他。

c3494790169d4a11984295207773384d.png

在上方搜索qt,下面的这个应用就是qt,点击打开他。

2、创建新的项目

3eebb96a077742abbd9ff7ee71e12404.png

进入之后点击New Project

50190fca24b443089f549d0a540c3120.png

之后选择第二个。

之后一路Choose即可,注意在此步的下一步是选择路径的,路径不能有中文

进入之后就是这样的:

31e4e61f31284035819f6c630cbbeb3e.png

智能指针的构建

点击我们的项目,创建新的文件:

d3c817013e39495f9736572a2355eb28.png

38c532352fa9496aa74077955550159b.png

选择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;
}


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

f64d81d539254b66bf6f5e082a83243d.png

具体的过程如图所示

拷贝构造函数:

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

相关文章
|
2天前
|
C++
C++野指针 空指针 危险指针
C++野指针 空指针 危险指针
14 3
|
2天前
|
算法 安全 程序员
C++ “万能血“ void*指针
C++ “万能血“ void*指针
8 1
|
2天前
|
算法 安全 C++
C++ Effective Modern Pointer (智能指针模块)
C++ Effective Modern Pointer (智能指针模块)
6 0
|
3天前
|
编译器 C语言 C++
C++类和对象的细节原理:this指针、构造函数和析构函数、深浅拷贝、运算符重载、初始化列表、类的各种成员和方法
C++类和对象的细节原理:this指针、构造函数和析构函数、深浅拷贝、运算符重载、初始化列表、类的各种成员和方法
19 0
|
3天前
|
安全 编译器 C语言
深入了解C++:形参、内联、重载、引用、const和指针、new和delete
深入了解C++:形参、内联、重载、引用、const和指针、new和delete
16 1
|
18天前
|
存储 程序员 C++
开心档之 C++ 指针
开心档之 C++ 指针
|
19天前
|
算法 测试技术 C++
【数据结构】模式匹配之KMP算法与Bug日志—C/C++实现
【数据结构】模式匹配之KMP算法与Bug日志—C/C++实现
23 0
|
19天前
|
安全 C++
C++智能指针的用法
C++智能指针的用法
22 0
|
19天前
|
存储 C++ Python
【数据结构】哈希表—C/C++实现
【数据结构】哈希表—C/C++实现
29 0
|
19天前
|
C++
C/C++工程师面试题(指针篇)
C/C++工程师面试题(指针篇)
12 0

相关产品

  • 云迁移中心