智能指针分析

目录

智能指针原理

STL中的智能指针

auto_ptr 

shared_ptr、weak_ptr

unique_ptr

慎用裸指针

Qt中的智能指针 


智能指针原理

智能指针:一个特殊的指针 ,是对裸指针进行包装的类,指针生命周期结束时主动释放堆空间 。

关键技术:操作符重载,实现类对象可以模拟指针的行为

// 智能指针的初步实现
#include <iostream>  
#include <string>  
  
using namespace std;  
  
class Test  
{  
    int i;  
public:  
    Test(int i)  
    {  
        cout << "Test(int i)" << endl;  
        this->i = i;  
    }  
    int value()  
    {  
        return i;  
    }  
    ~Test()  
    {  
        cout << "~Test()" << endl;  
    }  
};  
  
class Pointer  
{  
    Test* mp;  // 裸指针
public:  
    Pointer(Test* p = NULL)  
    {  
        mp = p;  
    }  
    Pointer(const Pointer& obj)   // 一片堆空间最多只能由一个指针指向
    {  
        mp = obj.mp;  
        const_cast<Pointer&>(obj).mp = NULL;  
    }  
    Pointer& operator = (const Pointer& obj)  
    {  
        if( this != &obj )  
        {  
            delete mp;  
            mp = obj.mp;  
            const_cast<Pointer&>(obj).mp = NULL;  
        }  
          
        return *this;  
    }  
    Test* operator -> ()  
    {  
        return mp;  
    }  
    Test& operator * ()  
    {  
        return *mp;  
    }  
    bool isNull()  
    {  
        return (mp == NULL);  
    }  
    ~Pointer()  
    {  
        delete mp;  //关键
    }  
};  
  
int main()  
{  
    Pointer p1 = new Test(0);  
      
    cout << p1->value() << endl;  
      
    Pointer p2 = p1;  
      
    cout << p1.isNull() << endl;  
      
    cout << p2->value() << endl;  
      
    return 0;  
} 

 

发现当智能指针发生拷贝构造后发生了资源转移,不能多个指针指向同一个堆对象,且智能指针只能指向堆内存,不能指向其他系统资源如文件等,不能完美的释放堆数组,局限性很大

解决方案: 基于引用计数的智能指针:一个堆对象可以被多个智能指针指向,当没有智能指针指向时释放堆空间,模板参数提供删除器选项,可根据需要传入任意自定义删除器

#include <iostream>
using namespace std;

template <typename T>
struct DeleteHeap
{
public:
    void operator () (T*& p)
    {
        delete p;
        p = NULL;
    }
};
template <typename T>
struct DeleteHeapArray
{
public:
    void operator () (T*& p)
    {
        delete[] p;
        p = NULL;
    }
};
template <typename T>
struct CloseFile
{
public:
    void operator () (FILE*& fp)
    {
        cout << "fclose(fp)" << endl;
        fclose(fp);
        fp = NULL;
    }
};
template <typename T, class Deleter = DeleteHeap<T>>
class SharedPointer
{
private:
    T* m_ptr;
    int* m_refCount;
public:
    SharedPointer() : m_ptr(nullptr), m_refCount(nullptr)
    {

    }
    explicit SharedPointer(T* ptr) : m_ptr(ptr), m_refCount(nullptr) // explict
    {
        if(m_ptr != nullptr)
        {
            m_refCount = new int(1);
        }
    }
    SharedPointer(const SharedPointer<T>& obj)
    {
        m_ptr = obj.m_ptr;
        m_refCount = obj.m_refCount;

        if(m_refCount != nullptr) // 引用计数指针不为空,说明拷贝对象不是一个空指针
        {
            (*m_refCount)++;
        }
    }

    SharedPointer<T>& operator = (const SharedPointer<T>& obj)
    {
        if(this != &obj)
        {
            reset();
            m_ptr = obj.m_ptr;
            m_refCount = obj.m_refCount;

            if(m_refCount != nullptr)
            {
                (*m_refCount)++;
            }
        }

        return *this;
    }

    // 返回引用计数
    int use_count()
    {
        if(m_refCount)
            return *m_refCount;
        return 0;
    }

    // 引用计数>0时,引用计数-1;指针置空,若引用计数为0 释放对象
    void reset()
    {
        T* toDel = m_ptr;
        int* ref = m_refCount;
        m_ptr = nullptr;
        m_refCount = nullptr;

        if(ref != nullptr)
        {
            (*ref)--;
            if(*ref == 0)
            {
                Deleter()(toDel);
                delete ref;
            }
        }
    }

    bool unique()
    {
        return (use_count() == 1);
    }

    T& operator * ()
    {
        return *m_ptr;
    }
    T* operator -> ()
    {
        return m_ptr;
    }
    T* get() const
    {
        return m_ptr;
    }

    // ...

    ~SharedPointer(){
        reset();
    }
};

class Test
{
    int m_i;
public:
    Test(int i = 0){
        m_i = i;
    }
    int getI() {
        return m_i;
    }
    ~Test(){
        cout << "~Test()" << " " << m_i << endl;
    }
};
int main()
{
    SharedPointer<Test> sp1(new Test(100)); // 必须直接初始化,是explict,不支持隐士类型转换
    SharedPointer<Test> sp2 = sp1;

    cout << "sp1: " << sp1.get() << endl; // 0158FDE8
    cout << "sp2: " << sp2.get() << endl; // 0158FDE8
    cout << "sp2.use_count(): " << sp2.use_count() << endl;     // 2
    sp1.reset();
    cout << "sp2.use_count(): " << sp2.use_count() << endl;      // 1
    cout << "sp2.unique(): " << sp2.unique() << endl;            // 1
    sp2.reset();

    SharedPointer<Test, DeleteHeapArray<Test>> sp3(new Test[2]);

    SharedPointer<FILE, CloseFile<FILE>> sp4(fopen("./test.txt", "w+"));

    fwrite("123", 3, 1, sp4.get());

    return 0;
}

STL中的智能指针

STL中的智能指针 :auto_ptr(C++98)、unique_ptr(C++11)、shared_ptr(C++11)、weak_ptr(C++11)

auto_ptr 

—片堆空间只属于—个智能指针对象 ,多个智能指针对象不能指向同—片堆空间 

#include <iostream>  
#include <string>  
#include <memory>  
  
using namespace std;  
  
class Test  
{  
    string m_name;  
public:  
    Test(const char* name)  
    {  
        cout << "Hello, " << name << "." << endl;  
          
        m_name = name;  
    }  
      
    void print()  
    {  
        cout << "I'm " << m_name << "." << endl;  
    }  
      
    ~Test()  
    {  
        cout << "Goodbye, " << m_name << "." << endl;  
    }  
};  
  
int main()  
{  
    auto_ptr<Test> pt(new Test("A"));  
      
    cout << "pt = " << pt.get() << endl;  
      
    pt->print();  
      
    cout << endl;  
      
    auto_ptr<Test> pt1(pt);  //所有权转让
      
    cout << "pt = " << pt.get() << endl;    // 0
    cout << "pt1 = " << pt1.get() << endl;  
      
    pt1->print();  
      
    return 0;  
} 

auto_ptr 的缺点

int main()
{
	int* p = new int(10);
	auto_ptr<int> ap1(p);

	auto_ptr<int> ap2 = ap1; // 拷贝构造、赋值操作时所有权转移

	cout << ap1.get() << endl;        // 0
	cout << (ap2.get() == p) << endl; // 1

	// auto_ptr的危害
	//cout << *ap1 << endl;  // 所有权已经转移,不应该再次操作(太不易察觉了,怎么解决呢?)

    // 慎用裸指针
	auto_ptr<int> ap3(p);  // 使用裸指针初始化两个智能指针对象,p指向的对象多次析构
	cout << (ap3.get() == ap2.get()) << endl;  // 1

	// auto_ptr的析构函数使用的是delete不是delete[],所以不应该用来管理一个数组
    
    // auto_ptr破坏了复制语义,不能放到标准容器中

	return 0;
}

shared_ptr、weak_ptr

shared_ptr : 带有引用计数机制,支持多个指针指向同一个对象,最后一个指针被销毁对象才销毁,原理参考前面代码

weak_ptr :配合shared_ptr而引入的—种智能指针,起监控作用,可以解决shared_ptr循环引用问题

#include <iostream>
#include <memory>
using namespace std;

class Test
{
	int m_i;
public:
	Test(int i = 0){
		m_i = i;
	}
	int getI() {
		return m_i;
	}
	~Test(){
		cout << "~Test()" << " " << m_i << endl;
	}
};
shared_ptr<int> make(int val)
{
	return shared_ptr<int>(new int(val));
}
template<typename T>
shared_ptr<T> make_shared_array(size_t size)
{
	return shared_ptr<T> p(new T[size], std::default_delete<T[]>());
}
void myDel(Test* p) 
{ 
	delete[] p;
}

void test_shared_ptr()
{
	shared_ptr<Test> sp1(new Test(1)); // 必须直接初始化,是explict,不支持隐士类型转换
	shared_ptr<Test> sp2 = sp1;

	cout << "sp1.get(): " << sp1.get() << endl; 
	cout << "sp2.get(): " << sp2.get() << endl;
	cout << "sp2.use_count():" << sp2.use_count() << endl; // 2
	sp1 = nullptr;
	cout << "sp2.use_count():" << sp2.use_count() << endl; // 1
	cout << "sp2.unique():" << sp2.unique() << endl;
	sp2.reset(); 
				  
	// make_shard(); // 在堆中分配并初始化一个对象并返回指向该对象的shared_ptr
	shared_ptr<Test> sp3 = make_shared<Test>(2);
	sp3 = make_shared<Test>(3); // 释放原来指向的堆对象,重新指向新对象
	sp3.reset();

	shared_ptr<int> sp4 = make_shared<int>(1000);
	shared_ptr<int> sp5 = make_shared<int>(); // 初始化为0
	cout << endl;
	cout << *sp4 << "  " << *sp5 << endl;
	sp5.swap(sp4); // 交换两个智能指针所指向对象
	cout << *sp4 << "  " << *sp5 << endl;
	cout << endl;

	// 指定删除器取代默认删除器(当管理动态数组时需要,手动delete[])
	shared_ptr<Test> p1(new Test[2], myDel); // 当引用计数为0时,调用myDel
	shared_ptr<int> p2(new int(1), [](int* p) {delete p; }); //删除器可以是lambda表达式
	shared_ptr<Test> p3(new Test[2], std::default_delete<Test[]>());
}

void test_weak_ptr()
{
	// 强指针:shared_ptr,弱指针:weak_ptr
	// weak_ptr指向由shared_ptr管理的对象,不会改变引用计数,起"监视作用"
	// lock(); 检查weak_ptr所指向的对象是否存在,存在返回指向该对象的shared_ptr,强引用计数+1,不存在返回空的shared_ptr;
	// use_count(); 监控资源的强引用计数
	// expired();  use_count == 0,测试所有权是否已过期,即是否已删除引用的对象
	// reset() 弱引用计数-1,不影响强引用

	shared_ptr<Test> sp1(new Test(100));
	weak_ptr<Test> wp1(sp1); // 强引用计数不变,弱引用计数+1, 弱引用计数并不影响堆对象生命周期
	cout << "sp1.use_count(): " << sp1.use_count() << endl; // 1
	cout << "wp1.use_count(): " << wp1.use_count() << endl; // 1

	shared_ptr<Test> sp2 = sp1; // 强引用计数+1

	cout << "sp1.use_count(): " << sp1.use_count() << endl; // 2
	cout << "wp1.use_count(): " << wp1.use_count() << endl; // 2

	weak_ptr<Test> wp2 = wp1; // 弱引用计数并不影响堆对象生命周期
	wp2.reset();
	wp1.reset();
	cout << "sp1.use_count(): " << sp1.use_count() << endl; // 2
	cout << "wp2.use_count(): " << wp2.use_count() << endl; // 0
	cout << endl;

	wp1 = sp1;

	if (!wp1.expired())
	{
		auto sp3 = wp1.lock(); // 强引用计数+1

		if (sp3 != nullptr){
			cout << "wp1.use_count(): " << wp1.use_count() << endl;
		}
	}
	else
	{
		cout << "use_count == 0,指针指向的对象已经不存在了" << endl;
	}

	cout << endl << endl;

	weak_ptr<Test> wp4;
	{
		auto sp4 = make_shared<Test>(200);
		wp4 = sp4;
		cout << "wp4.use_count()" << wp4.use_count() << endl;
	} // 执行到这里,sp4指向的对象销毁,wp4过期
	if (!wp4.expired()) {

	}
	else {
		cout << "sp4指向的对象已经销毁"<< endl;
	}
}

// 循环引用
class Node
{
	int value;
public:
	Node(int val = 0) {
		value = val;
	}
	shared_ptr<Node> prev;
	shared_ptr<Node> next;
	~Node() {
		cout << "~Node() : " << value << endl;;
	}
};
// 循环引用解决方案
class NodeOK
{
	int value;
public:
	NodeOK(int val = 0) {
		value = val;
	}
	weak_ptr<NodeOK> prev;
	shared_ptr<NodeOK> next;
	~NodeOK() {
		cout << "~NodeOK() : " << value << endl;;
	}
};


void test_circle() 
{
	{
		shared_ptr<Node> sp1(new Node(100));
		shared_ptr<Node> sp2(new Node(200));

		cout << sp1.use_count() << endl; // Node(100)引用计数为 1
		cout << sp2.use_count() << endl; // Node(200)引用计数为 1

		sp1->next = sp2; // Node(200)引用计数为 2
		sp2->prev = sp1; // Node(100)引用计数为 2

		cout << sp1.use_count() << endl;
		cout << sp2.use_count() << endl;

		// sp2局部变量析构,sp1局部变量析构,Node(200)引和Node(100)引用计数都变为1
		// 最终结果:Node(100) 和 Node(200)都没有被析构
	}
	cout << endl;
	{
		shared_ptr<NodeOK> sp1(new NodeOK(100));
		shared_ptr<NodeOK> sp2(new NodeOK(200));

		cout << sp1.use_count() << endl; // NodeOK(100)引用计数为 1
		cout << sp2.use_count() << endl; // NodeOK(200)引用计数为 1

		sp1->next = sp2; // NodeOK(200)引用计数为 2
		sp2->prev = sp1; // NodeOK(100)引用计数为 1(弱引用不会改变引用计数)

		cout << sp1.use_count() << endl; // 1
		cout << sp2.use_count() << endl; // 2

		// sp2局部变量析构,NodeOK(200)引用计数变为1
		// sp1局部变量析构,NodeOK(100)引用计数变为0, NodeOK(200)引用计数变为0
	}
}


int main()
{
	test_shared_ptr();
	cout << "---------------------------------------" <<endl;
	test_weak_ptr();
	cout << "---------------------------------------" << endl;
	test_circle();

	return 0;
}

unique_ptr

—个指针对象指向—片内存空间,同一时间只能有一个指针可以指向,不能拷贝构造和赋值, 但支持移动语句

unique_ptr作为函数参数或返回值不是拷贝构造而是发生了移动语义

auto_ptr直接调用拷贝构造实现资源转移,不易察觉,易出错。

而unique_ptr禁用了拷贝构造使用std::move()或移动构造实现实现资源转移,直观明了

class unique_ptr {
public:
    unique_ptr();
    unique_ptr(nullptr_t Nptr);
    explicit unique_ptr(pointer Ptr);
    unique_ptr(pointer Ptr,
        typename conditional<is_reference<Del>::value, Del,
        typename add_reference<const Del>::type>::type Deleter);
    unique_ptr(pointer Ptr,
        typename remove_reference<Del>::type&& Deleter);
    unique_ptr(unique_ptr&& Right);
    template <class T2, Class Del2>
    unique_ptr(unique_ptr<T2, Del2>&& Right);
    unique_ptr(const unique_ptr& Right) = delete;  // 禁用拷贝构造和复制操作
    unique_ptr& operator=(const unique_ptr& Right) = delete;
};

//Specialization for arrays:
template <class T, class D>
class unique_ptr<T[], D> {
public:
    typedef pointer;
    typedef T element_type;
    typedef D deleter_type;
    constexpr unique_ptr() noexcept;
    template <class U>
    explicit unique_ptr(U p) noexcept;
    template <class U>
    unique_ptr(U p, see below d) noexcept;
    template <class U>
    unique_ptr(U p, see below d) noexcept;
    unique_ptr(unique_ptr&& u) noexcept;
    constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }     template <class U, class E>
        unique_ptr(unique_ptr<U, E>&& u) noexcept;
    ~unique_ptr();
    unique_ptr& operator=(unique_ptr&& u) noexcept;
    template <class U, class E>
    unique_ptr& operator=(unique_ptr<U, E>&& u) noexcept;
    unique_ptr& operator=(nullptr_t) noexcept;
    T& operator[](size_t i) const;

    pointer get() const noexcept;
    deleter_type& get_deleter() noexcept;
    const deleter_type& get_deleter() const noexcept;
    explicit operator bool() const noexcept;
    pointer release() noexcept;
    void reset(pointer p = pointer()) noexcept;
    void reset(nullptr_t = nullptr) noexcept;
    template <class U>
    void reset(U p) noexcept = delete;
    void swap(unique_ptr& u) noexcept;  // disable copy from lvalue unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;
};
#include <iostream>
#include <memory>
using namespace std;

class Test
{
	int m_i;
public:
	Test(int i = 0){
		m_i = i;
	}
	int getI() {
		return m_i;
	}
	~Test() {
		cout << "~Test()" << " " << m_i << endl;
	}
};

void test_unique_ptr()
{
	unique_ptr<int> up1 = make_unique<int>(100);// C++14才有make_unique
	unique_ptr<int> up2(new int(200)); // explict
	// p1 = p2; // error

	unique_ptr<int> up3(up1.release()); // 放弃指针控制权,返回裸指针,同时将该智能指针置空
	cout << (up1 == nullptr) << endl;   // 1
	up3.reset(); // 释放智能指针指向的堆对象,并置空

	// 使用移动语义比auto_ptr更能体现资源的移交
	unique_ptr<int> up4 = std::move(up2);
	cout << up2.get() << " " << up4.get() << endl;

	// unique_ptr可以指向堆数组
	unique_ptr<Test[]> up5(new Test[2]);
}


int main()
{
	test_unique_ptr();
	return 0;
}

慎用裸指针

#include <iostream>
#include <memory>
using namespace std;

class Test 
{
	int m_i;
public:
	Test(int i = 0) {
		m_i = i;
	}
	shared_ptr<Test> getSelf()
	{
		return shared_ptr<Test>(this);
	}
	~Test() {
		cout << "~Test()" << " " << m_i << endl;
	}
};

void test_unique_ptr()
{
	unique_ptr<int> up1 = make_unique<int>(100);// C++14才有make_unique
	unique_ptr<int> up2(new int(200)); // explict
	// p1 = p2; // error

	unique_ptr<int> up3(up1.release()); // 放弃指针控制权,返回裸指针,同时将该智能指针置空
	cout << (up1 == nullptr) << endl;   // 1
	up3.reset(); // 释放智能指针指向的堆对象,并置空

	// 使用移动语义比auto_ptr更能体现资源的移交
	unique_ptr<int> up4 = std::move(up2);
	cout << up2.get() << " " << up4.get() << endl;

	// unique_ptr可以指向堆数组
	unique_ptr<Test[]> up5(new Test[2]);
}

void func(shared_ptr<int> ptr) {}

void test1()
{
	int* p = new int(10);

	//func(shared_ptr<int>(p)); // 函数形参销毁,引用计数由1变为0,对象被销毁
	//*p = 1;                   // 发生错误

	// 安全的使用方式
	shared_ptr<int> p2(p); // 内存管理交给p2
	func(p2);
	*p2 = 445;
}
void test2()
{
	int* p = new int(10);
	// 不要用裸指针初始化多个shared_ptr
	shared_ptr<int> p1(p);
	//shared_ptr<int> p2(p);// 引用计数都为1,内存会释放两次

	shared_ptr<int> p2(p1); // 正确安全的使用方式
}

void test3()
{
	shared_ptr<int> p1(new int(10));
	{
		shared_ptr<int> p2(p1.get()); 
		// 执行完这里,p1.get()被释放
	}
	*p1 = 1; // error
}

void test4()
{
	shared_ptr<Test> sp1(new Test(100));
	shared_ptr<Test> sp2 = sp1->getSelf();
	// 上面出错代码等价于下面
	Test* p = new Test(200);
	shared_ptr<Test> p1(p);
	shared_ptr<Test> p2(p);
	shared_ptr<Test> p3(p2);
	// 如何解决?
	// 可以这样 class Test : public enable_shared_from_this<Test>
	// 然后这样 shared_ptr<Test> getself() { return shared_from_this();/*内部调用了弱指针的lock()*/; }
}
int main()
{
	return 0;
}

 

Qt中的智能指针 

QPointer :当其指向的对象被销毁时,它会被自动置空 ,析构时不会自动销毁所指向的对象 

QSharedPointer :引用计数型智能指针 ,可以被自由地拷贝和赋值 ,当引用计数为0时才删除指向的对象 

#include <QPointer>  
#include <QSharedPointer>  
#include <QDebug>  
  
class Test : public QObject  
{  
    QString m_name;  
public:  
    Test(const char* name)  
    {  
        qDebug() << "Hello, " << name << ".";  
  
        m_name = name;  
    }  
  
    void print()  
    {  
        qDebug() << "I'm " << m_name << ".";  
    }  
  
    ~Test()  
    {  
        qDebug() << "Goodbye, " << m_name << ".";  
    }  
};  
  
int main()  
{  
    QPointer<Test> pt(new Test("Nyist"));  
    QPointer<Test> pt1(pt);  
    QPointer<Test> pt2(pt);  
  
    pt->print();  
    pt1->print();  
    pt2->print();  
  
    delete pt;      //不会自动调用析构函数   
  
    qDebug() << "pt = " << pt;  
    qDebug() << "pt1 = " << pt1;  
    qDebug() << "pt2 = " << pt2;  
  
    qDebug() << endl;  
  
    QSharedPointer<Test> spt(new Test("Nysit"));  //引用计数+1   
    QSharedPointer<Test> spt1(spt);  
    QSharedPointer<Test> spt2(spt);           //此时引用计数为3   
  
    spt->print();              
    spt1->print();  
    spt2->print();  
                    //—1 ,-1,-1   
    return 0;       //销毁后引用计数  = 0  
}  

Qt中的其它智能指针 :QWeakPointer 、QScopedPointer 、QScopedArrayPointer 、QSharedDataPointer 、QExplicitlySharedDataPointer

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值