实现智能指针

  c/c++中内存的动态分配的结果是一个指针,该指针指向该片堆内存空间。c/c++中并没有垃圾回收机制,而用户使用完该堆空间后又可能忘记释放该片堆内存空间,这就造成内存泄漏。一个行之有效的解决办法就是采用智能指针代替原始指针,弥补原生指针无法控制所指堆空间的生命期的空缺。
  在c++11标准中有多个智能指针,其中包括共享指针shared_ptr和独占指针unique_ptr。本文将实现SharedPointer和UniquePointer来效仿这两个智能指针。
  类的继承结构UML图如下:
这里写图片描述
  Pointer是一个基类,封装了SharedPointer和UniquePointer共有的操作函数:

//Pointer.h
#ifndef __POINTER_H__
#define __POINTER_H__

template<typename T>
class Pointer
{
protected:
    T* m_pointer;   //指向用户动态分配的内存空间

public:
    //构造函数,为m_pointer赋值
    Pointer(T* p = NULL) { m_pointer = p; }     

    //指针关联的操作符的重载函数
    T* operator->() { return m_pointer; }       
    T& operator*() { return *m_pointer; } 

    //为const对象使用
    T* operator->() const { return m_pointer; } 
    T& operator*() const { return *m_pointer; }

    bool isNull() const { return m_pointer == NULL; }
    T* get() const { return m_pointer;  }

    virtual ~Pointer() {}
};

#endif /* __POINTER_H__ */

  UniquePointer指针称为独占指针,其设计要点为:
  (1)指针生命周期结束(所在函数退出)时主动释放堆空间
  (2)一片堆空间至多只能由一个指针标识

#ifndef __UNIQUEPOINTER_H__
#define __UNIQUEPOINTER_H__

#include "Pointer.h"

template<typename T>
class UniquePointer : public Pointer<T>
{
public:
    UniquePointer(T* p = NULL) : Pointer<T>(p)  //构造函数调用父类的构造函数
    {}

    UniquePointer(const UniquePointer<T>& obj)  //拷贝构造函数
    {
        //确保obj.m_pointer指向的堆空间只能由一个UniquePointer指针指向
        this->m_pointer = obj.m_pointer;
        const_cast<UniquePointer<T>& >(obj).m_pointer = NULL;
    }

    UniquePointer<T>& operator=(const UniquePointer<T>& obj)
    {
        if (this != &obj)
        {
            T* p = this->m_pointer;

            //确保obj.m_pointer指向的堆空间只能由一个UniquePointer指针指向
            this->m_pointer = obj.m_pointer;
            const_cast<UniquePointer<T>& >(obj).m_pointer = NULL;

            //最后在delete,确保异常安全
            delete p;
        }
        return *this;
    }

    ~UniquePointer()
    {
        //释放堆空间
        delete this->m_pointer;
    }
};

#endif /* __UNIQUEPOINTER_H__ */

  使用UniquePointer指针时需要注意:
  (1)只能使用UniquePointer来指向堆空间中单个对象或者变量的内存空间,不能指向数组空间(析构函数是delete而非delete[])
  (2)交付UniquePointer的指针不能手动delete(否则会因在UniquePointer析构函数再delete而运行时崩溃)

//main.cpp
#include <iostream>
#include "UniquePointer.h"
#include "SharedPointer.h"

using namespace std;

class Ctest
{
public:
    int a;
    Ctest(int m) : a(m) { cout << "Ctest()" << endl; }
    ~Ctest() { cout << "~Ctest()" << endl; }  
};

int main(void)
{
    UniquePointer<Ctest> upc1 = new Ctest(6);
    cout << "upc1.get() = " << upc1.get() << endl;
    UniquePointer<Ctest> upc2 = upc1;
    cout << "upc1.get() = " << upc1.get() << endl;
    cout << "upc2.get() = " << upc2.get() << endl; 

    return 0;
}

  makefile:

SRC = $(wildcard *.cpp)
OBJ = $(patsubst %.cpp, %.o, $(SRC))

all : clean a.out

a.out : main.o
    g++ $< -o $@ -Wall

%.o : %.cpp
    g++ -c $< -o $@ -Wall

.PHONY : clean
clean:
    rm -rf $(OBJ) a.out

  编译运行:
这里写图片描述

  SharedPointer指针称为共享指针,其实现要点为:
  (1)通过计数机制来标识指向堆内存的指针的个数:指向一块堆内存时,该堆空间的计数值计数加1;指针被置空或者指向其它堆内存时,原堆空间的计数值减1;计数值为0时(无指针指向该堆内存)释放堆内存。
  (2)因为一片堆空间可由多个SharedPointer指针指向,所以允许SharedPointer类之间的==和!=操作,以判断两个SharedPointer对象是否指向同一片堆空间。

//SharedPointer.h
#ifndef __SHAREDPOINTER_H__
#define __SHAREDPOINTER_H__

#include <stdlib.h>
#include <stdexcept>
#include "Pointer.h"

template<typename T>
class SharedPointer : public Pointer<T>
{
protected:
    int *m_ref;

    void _clear()   //递减计数值,置空m_pointer和m_ref。若计数值为0则销毁指向的堆空间
    {
        T* toDel = this->m_pointer;
        int* ref = this->m_ref;
        this->m_pointer = NULL;
        this->m_ref = NULL;

        if (ref)
        {
            --(*ref);
            if (*ref == 0)
            {
                free(ref);
                //为确保异常全,最后才delete
                delete toDel;
            }
        }
    }

public:
    //确保异常安全,在this->m_ref动态分配成功后再为基类的m_pointer赋值
    SharedPointer(T* p = NULL) : /*Pointer<T>(p), */m_ref(NULL)
    {
        if (p)
        {
            this->m_ref = static_cast<int* >(malloc(sizeof(int)));
            if (this->m_ref)
            {
                *(this->m_ref) = 1;
                this->m_pointer = p;
            }
            else
            {
                throw(std::out_of_range("SharedPointer(T* p = NULL): malloc memory failed"));
            }
        }
    }

    SharedPointer(const SharedPointer<T>& obj)
    {
        //指针指向obj所维护的堆空间,并自加该堆空间的引用计数
        this->m_ref = obj.m_ref;
        this->m_pointer = obj.m_pointer;

        if (this->m_ref)
            ++(*(this->m_ref));
    }

    SharedPointer<T>& operator=(const SharedPointer<T>& obj)
    {
        if (this != &obj)
        {
            //先清除自身维护的堆空间
            _clear();

            //再指向obj所维护的堆空间,并自加该堆空间的引用计数
            this->m_ref = obj.m_ref;
            this->m_pointer = obj.m_pointer;

            if (this->m_ref)
                ++(*(this->m_ref));
        }
        return *this;
    }

    ~SharedPointer()
    {
        _clear();
    }
};

//SharedPointer的比较函数。一般比较函数会设计为全局函数
template <typename T>
bool operator==(const SharedPointer<T>& left, const SharedPointer<T>& right)
{
    return (left.get() == right.get());
}

template <typename T>
bool operator!=(const SharedPointer<T>& left, const SharedPointer<T>& right)
{
    //return !(left.get() == right.get());
    return !(left == right);
}

#endif /* __SHAREDPOINTER_H__ */

  使用SharedPointer指针:

int main(void)
{
    const SharedPointer<Ctest> spc1 = new Ctest(6); 
    const SharedPointer<Ctest> spc2 = spc1;
    SharedPointer<Ctest> spc3;

    cout << spc2->a << endl;
    cout << "spc1.get() = " << spc1.get() << endl;
    cout << "spc2.get() = " << spc2.get() << endl;
    cout << "spc3.get() = " << spc3.get() << endl;

    if (spc2 != spc1 )
        cout << "spc3 != spc1" << endl;
    else
        cout << "spc3 == spc1" << endl; 

    return 0;
}

  编译运行:
这里写图片描述
  SharedPointer的使用注意项和UniquePointer的类似,另外还需要注意不同类型的智能指针不可以混合使用。

猜你喜欢

转载自blog.csdn.net/qq_29344757/article/details/79237738
今日推荐