zoukankan      html  css  js  c++  java
  • C++智能指针

    1.前言

    C++里面的四个智能指针: auto_ptr, unique_ptr,shared_ptr, weak_ptr 其中后三个是C++11支持,并且第一个已经被C++11弃用。

    2.简介

    智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。C++ 11中最常用的智能指针类型为shared_ptr,它采用引用计数的方法,记录当前内存资源被多少个智能指针引用。该引用计数的内存在堆上分配。当新增一个时引用计数加1,当过期时引用计数减一。只有引用计数为0时,智能指针才会自动释放引用的内存资源。对shared_ptr进行初始化时不能将一个普通指针直接赋值给智能指针,因为一个是指针,一个是类。可以通过make_shared函数或者通过构造函数传入普通指针。并可以通过get函数获得普通指针。

    3.为什么使用智能指针

    智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。手动管理内存带来的更严重的问题是,内存究竟要由谁来分配和释放呢?指针的赋值将同一对象的引用散播到程序各处,但是该对象的释放却只能发生一次。当在代码中用完了一个资源指针,该不该释放 delete 掉它?这个资源极有可能同时被多个对象拥有着,而这些对象中的任何一个都有可能在之后使用该资源,其余指向这个对象的指针就变成了“野指针”;那如果不 delete 呢?也许你就是这个资源指针的唯一使用者,如果你用完不 delete,内存就泄漏了。

    资源的拥有者是系统,当我们需要时便向系统申请资源,当我们不需要时就让系统自己收回去(Garbage Collection)。当我们自己处理的时候,就容易出现各种各样的问题。

    使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。

    4.share_ptr

    所在头文件: #include <memory>
    //初始化:
          int a = new int(100);
          std::shared_ptr ptr(a);
    
    //      使用make_shared初始化
          std::shared_ptr<int> ptr1 = std::make_shared<int>(15);
    
    
    //综合案例
    #include <iostream>
    #include <string>
    #include <memory>
    
    using namespace std;
    class Test
    {
    public:
        Test(string name)
        {
            name_ = name;
            cout << this->name_ << "  constructor" << endl;
        }
        ~Test()
        {
            cout << this->name_ << "  destructor" << endl;
        }
    
        string name_;
    };
    
    
    int main()
    {
        /* 类对象 原生指针构造 */
        shared_ptr<Test> pStr1(new Test("object"));
        cout << (*pStr1).name_ << endl;
        /* use_count()检查引用计数 */
        cout << "pStr1 引用计数:" << pStr1.use_count() << endl;
    
        shared_ptr<Test> pStr2 = pStr1;
        cout << (*pStr2).name_ << endl;
        cout << "pStr1 引用计数:" << pStr1.use_count() << endl;
        cout << "pStr2 引用计数:" << pStr2.use_count() << endl;
        
        /* 先new 一个对象,把原始指针传递给shared_ptr的构造函数 */
        int *pInt1 = new int(11);
        shared_ptr<int> pInt2(pInt1);
    
        /* unique()来检查某个shared_ptr 是否是原始指针唯一拥有者 */
        cout << pInt2.unique() << endl; //true 1
        /* 用一个shared_ptr对象来初始化另一个shared_ptr实例 */
        shared_ptr<int> pInt3(pInt2);
        cout << pInt2.unique() << endl; //false 0
        
        cout << pInt3.use_count() << endl;
        cout << pInt2.use_count() << endl;
        return 0;
    }
    
    结果:
    object constructor
    object 
    pStr1 引用计数:1
    object 
    pStr1 引用计数:2
    pStr2 引用计数:2
    1
    0
    2
    2
    object destructor
    
    //判断是否为NULL,可使用get函数
    std::shared_ptr<int> ptr(new int(100));
    if (ptr.get()) {
        std::cout << "ptr is not null" << std::endl;
    } else {
        std::cout << "ptr is null" << std::enel;
    }
    
    #include <iostream>
    #include <memory>
     
     
    using namespace std;
     
    class Student : public enable_shared_from_this<Student>
    {
    public:
            Student() {}
    	~Student()
    	{
    		std::cout << "~Student被调用" << std::endl;	
    	}
    	std::shared_ptr<Student> getStudent()
    	{
    		return shared_from_this();	
    	} 
    	std::string name;
    	void setName(std::string name);
    	std::string getName();
    };
     
    void Student::setName(std::string name)
    {
    	this->name = name;	
    }
     
    std::string Student::getName()
    {
    	return name;	
    }
     
    int main()
    {
    	int *p = new int(10);	
    	//std::shared_ptr<int> ptr = p;这样赋值是错误的额,只要是智能指针,这样直接用=赋值是有问题的必须std::shared_ptr<int> ptr(p);
    	std::shared_ptr<int> ptr(p);
    	std::shared_ptr<int> ptr1 = std::make_shared<int>(15);
    	std::shared_ptr<int> ptr2(ptr1);
    	//std::shared_ptr<int> ptr2 = ptr1;这样赋值是错误的,只要是智能指针,这样直接用=赋值是有问题的必须std::shared_ptr<int> ptr2(ptr1);
    	std::cout << "ptr.use_count() is:" << ptr.use_count() << "  *ptr is:" << *ptr << std::endl;
    	std::cout << "ptr1.use_count() is:" << ptr1.use_count() << "  *ptr1 is:" << *ptr1 << std::endl;
    	std::cout << "ptr2.use_count() is:" << ptr2.use_count() << "  *ptr2 is:" << *ptr2 << std::endl;
    	
    	ptr2.reset();
    	//这是时候ptr2已经销毁,指向的对象引用计数会减1,这个指针的不再指向任何对象,所以我们不能使用*ptr2了,下面一行代码使用肯定会报错,我先注释掉
    	//std::cout << "ptr2.use_count() is:" << ptr2.use_count() << "*ptr2 is:" << *ptr2 << std::endl;
            std::cout << "ptr1.use_count() is:" << ptr1.use_count() << "   *ptr1 is:" << *ptr1 << std::endl;
    	Student *stu = new Student();
    	std::shared_ptr<Student> ptr_stu(stu);
    	std::string name = "chenyu";
    	ptr_stu->setName(name);
    	std::string result = ptr_stu->getName();
            std::cout << "ptr_stu.use_count() is:" << ptr_stu.use_count() << std::endl;
    	std::cout << "my name is:" << result << std::endl;
            return 0;
    }
    
    //结果为:
    ptr.use_count() is:1  *ptr is:10
    ptr1.use_count() is:2  *ptr1 is:15
    ptr2.use_count() is:2  *ptr2 is:15
    ptr1.use_count() is:1   *ptr1 is:15
    ptr_stu.use_count() is:1
    my name is:chenyu
    ~Student被调用
    

    再来看两个例子:

    #include <iostream>
    #include <vector>
    #include <memory>
    using namespace std;
    class test 
    {
    public:
        test(int x_, int y_) : x(x_), y(y_) {}
        void show(void)
        {
            cout << "x: " << x << "   y: " << y << endl;
        }
        ~test()
        {
            cout << "leave" << endl;
        }
    private:
        int x;
        int y;
    };
    
    int main()
    {
    
    {
        vector<shared_ptr<test>> sub;
    
        for(int i=0; i<10; i++)
        {
            shared_ptr<test> t(new test(i,i+1)); 
            sub.push_back(t); 
           //cout << "hello" << endl;
        }
    
        cout << "start" << endl;
    
        for(auto t : sub)
        {
            t->show();
        }
    }
    
    cout << "end" << endl;
    
    }
    

    运算结果为:

    start
    x: 0   y: 1
    x: 1   y: 2
    x: 2   y: 3
    x: 3   y: 4
    x: 4   y: 5
    x: 5   y: 6
    x: 6   y: 7
    x: 7   y: 8
    x: 8   y: 9
    x: 9   y: 10
    leave
    leave
    leave
    leave
    leave
    leave
    leave
    leave
    leave
    leave
    end
    

    可以看到因为t被sub引用,所以用new申请的空间会在sub失效后释放掉

    将中间的循环改为:

          shared_ptr<test> t(new test(i,i+1)); 
            //sub.push_back(t); 
          cout << "hello" << endl;
    

    结果为:

    hello
    leave
    hello
    leave
    hello
    leave
    hello
    leave
    hello
    leave
    hello
    leave
    hello
    leave
    hello
    leave
    hello
    leave
    hello
    leave
    start
    end
    

    因为t没有被引用,所以在每一次for循环就会释放空间

  • 相关阅读:
    视频编码之释——从H.261 到H.264
    bitmap图像介绍
    用搜索引擎搜索我的名字 @_@
    blog标题由来
    ORACLE双机热备安装及物理迁移 for win2000
    审核再次失败
    asp.net学习历程
    痛并快乐着
    开心,blog点击率超过1000
    XP下ASP.NET不能访问ORACLE数据库的解决方案
  • 原文地址:https://www.cnblogs.com/penuel/p/13143584.html
Copyright © 2011-2022 走看看