C++之智能指针

c++智能指针

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

智能指针从书面意思来说,就是智能。主要是动态内存的使用很容易出问题,要在正确的时间正确释放内存是很困难的。有时我们可能忘了释放内存,会导致内存泄漏;有时我们使用一个已经释放了的内存的指针,会产生引用非法内存的指针。
所以,使用智能指针就可以避免这种情况的方式。

1.auto_ptr类

虽然这个类已经被c++11弃用了,还是简单了解下

#include <iostream>
#include <memory>
using namespace std;
class m_string
{
    friend ostream& operator <<(ostream& os,const m_string& _string);
public:
    m_string(string s)
    {
        str = s;
        cout<<"creat m_string \n";
    }
    ~m_string()
    {
        cout<<"delete m_string:"<<str<<endl;
    }
    string& getStr()
    {
        return str;
    }
    void setStr(string s)
    {
        str = s;
    }
    void print()
    {
        cout<<str<<endl;
    }
private:
    string str;
};
ostream& operator <<(ostream& os,const m_string& _string)
{
    os<<_string.str;
    return os;
}
int main()
{
    auto_ptr<m_string> pString(new m_string("test"));
    pString->setStr("hi ");
    cout<<*pString<<endl;
    pString.get()->print();
    pString->getStr() += "xiaoming !";
    cout<<*pString<<endl;
    pString.reset(new m_string("test"));
    cout<<*pString<<endl;
    return 0;
}

这里写图片描述

其中,get()是auto_ptr类的成员函数,返回一个原始指针,成员函数reset()重新绑定指向的对象,会释放原来的对象。


int main()
{
    auto_ptr<m_string> p1(new m_string("hi"));
    auto_ptr<m_string> p2(new m_string("xiaoming"));
    p2 = p1;
    cout<<*p2<<endl;
    if(p1.get() == NULL)cout<<"p1 = NULL\n";
    return 0;
}

这里写图片描述
可以看出来,使用 = 进行辅助的时候,p2之前的对象会被释放,p2接管p1的内存管理权,p1变成了空指针,而且判断智能指针是否为空,应该使用if(p1.get() == NULL)。这样一来,感觉auto_ptr有不小的问题。


使用release()释放

int main()
{
    auto_ptr<m_string> p1(new m_string("123"));

    p1.release();
    return 0;
}

这里写图片描述
release()只是放弃了原来对象的内存管理权,并没有释放对象的内存,要释放对象的内存,应该使用p1.reset();,是不是感觉很奇怪?难怪c++11要弃用它。

2.share_ptr类

官方文档
share是共享的意思,它使用计数机制来表明有几个指针管理这个资源,使用use_count()成员函数查看资源所有者个数。当指向一个对象的最后一个share_ptr被销毁的时候,share_ptr类会自动销毁此对象。

  • 这是share_ptr的一些初始化方法
int main()
{
    shared_ptr<m_string> p1(new m_string("p1"));
    shared_ptr<m_string> p2 = make_shared<m_string>("p2");
    auto p3 = make_shared<m_string>("p3");
    auto p4 = p3;
    auto p5 = make_shared<pair<m_string,m_string>>( m_string("p5_1"), m_string("p5_2"));

    cout<<*p1<<":"<<p1.use_count()<< endl;
    cout<<*p2<<":"<<p2.use_count()<< endl;
    cout<<*p3<<":"<<p3.use_count()<< endl;
    cout<<*p4<<":"<<p4.use_count()<< endl;
    cout<<p5->first<<":"<<p5->second<<":"<<p5.use_count()<< endl;
    return  0;
}

使用make_shared函数,是最安全的分配和使用动态内存的方法。

  • make_shared的赋值
int main()
{
    std::shared_ptr<m_string> p1;
    std::shared_ptr<m_string> p2 (new m_string("p2"));

    //    cout<<"*p1: "<<*p1<<endl;

    //p1一开始是空的,现在使p1也指向p2的对象,这个时候,因为有两个智能指针指向同一个对象
    //使用shared_ptr类的计数器为2.
    p1 = p2;                          // copy

    cout<<"*p1: "<<*p1<<" "<<p1.use_count()<<endl;
    cout<<"*p2: "<<*p2<<" "<<p2.use_count()<<endl;
    cout<<endl;
    //cout<<"*p2: "<<*p2<<endl;

    //p2指向另外一个对象,这之前对象的计数器减1,即p1的计数器为1
    //p2因为指向一个新的对象,所以计数器也是为1
    p2 = std::make_shared<m_string> ("p2_1");   // move
    cout<<"*p1: "<<*p1<<" "<<p1.use_count()<<endl;
    cout<<"*p2: "<<*p2<<" "<<p2.use_count()<<endl;
    cout<<endl;

    //p1指向另外一个对象,这之前对象的计数器减1,这个时候,之前对象的计数器减少为1,所以调用m_string的解析函数,释放内存
    //p1因为指向一个新的对象,所以计数器也是为1
    std::unique_ptr<m_string> unique (new m_string("p3_unique"));
    p1 = std::move(unique);            // move from unique_ptr

    cout<<"*p1: "<<*p1<<" "<<p1.use_count()<<endl;
    cout<<"*p2: "<<*p2<<" "<<p2.use_count()<<endl;
    cout<<endl;
    return  0;
}

这里写图片描述
当p1和p2都没有指向之前的对象的时候,计数器为0,释放对象的内存。

  • 成员函数swap()
int main()
{
    shared_ptr<m_string> p1 = make_shared<m_string>("p1");
    shared_ptr<m_string> p2 = make_shared<m_string>("p2");
    cout<<"*p1: "<<*p1<<" "<<p1.use_count()<<endl;
    cout<<"*p2: "<<*p2<<" "<<p2.use_count()<<endl;
    p1.swap(p2);
    cout<<"*p1: "<<*p1<<" "<<p1.use_count()<<endl;
    cout<<"*p2: "<<*p2<<" "<<p2.use_count()<<endl;
    return 0;
}

这里写图片描述

3.unique_ptr类

官方文档
要强制销毁unique_ptr类指向的对象,应该使用成员函数reset()或者对其进行赋值操作

int main()
{
    unique_ptr<m_string> p1(new m_string("p1"));
    //将所有权从p1(p1指向的m_string对象)转移给p2,release将p1制空
    unique_ptr<m_string> p2(p1.release());
    if(p1 == nullptr)
        cout<<"p1 is nullptr"<<endl;

    cout<<"*p2:"<<*p2<<endl;
    //对p2中的str重新复制成"p2"
    p2->setStr("p2");
    cout<<"*p2:"<<*p2<<endl;
    cout<<"------------\n";

    unique_ptr<m_string> p3(new m_string("p3"));
    //p2使用成员函数reset()先释放p2原来指向对象的内存
    p2.reset(p3.release());
    cout<<"*p2:"<<*p2<<endl;
    cout<<"------------\n";

    //不能直接使用p2.release(),p2不会释放内存,会丢失了指针, 需要一个指针来接管这个内存管理权,
    auto p = p2.release();
    cout<<"*p:"<<*p<<endl;
    delete p;
    cout<<"------------\n";



    unique_ptr<m_string> p4(new m_string("p4"));
    unique_ptr<m_string> p5(new m_string("p5"));
    //不能使用p4 = p5;
    //不能使用 p5 = p4.release();
    p5 = std::move(p4);
    if(p4 == nullptr)
        cout<<"p4 is nullptr"<<endl;
    cout<<"*p5:"<<*p5<<endl;
    cout<<"------------\n";

    //unique_ptr类的成员函数swap
    unique_ptr<m_string> p6(new m_string("p6"));
    unique_ptr<m_string> p7(new m_string("p7"));
    p6.swap(p7);
    cout<<"*p6:"<<*p6<<endl;
    cout<<"*p7:"<<*p7<<endl;
    cout<<"------------\n";

    //判断是否为空
    std::unique_ptr<m_string> p8;
    std::unique_ptr<m_string> p9 (new m_string("p9"));

    if (p8) std::cout << "p8 points to " << *p8 << '\n';
    else std::cout << "p8 is empty\n";

    if (p9) std::cout << "p9 points to " << *p9 << '\n';
    else std::cout << "p9 is empty\n";
    cout<<"------------\n";

    return  0;

}

这里写图片描述

4.weak_ptr类

官方文档

  • std::weak_ptr 可以用来避免 std::shared_ptr 的循环引用
  • weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。
  • weak_ptr绑定到一个shared_ptr 不会改变shared_ptr 的引用计数。如果最后一个指向对象的shared_ptr 被销毁,那么对象会释放,及时weak_ptr指向该对象,对象也会释放。

class B;
class A
{
public:
    shared_ptr<B> _pb;
    ~A(){cout<<"delete A\n";}
};
class B
{
public:
    shared_ptr<A> _pa;
    ~B(){cout<<"delete B\n";}
};


int test12()
{
    shared_ptr<A> pa= make_shared<A>();
    shared_ptr<B> pb = make_shared<B>();

    pa->_pb = pb;
    pb->_pa = pa;
    cout<<pa.use_count()<<endl;
    cout<<pb.use_count()<<endl;
    return 0;
}

class B_2;
class A_2
{
public:
    shared_ptr<B_2> _pb;
    ~A_2(){cout<<"delete A_2\n";}
};
class B_2
{
public:
    weak_ptr<A_2> _pa;
    ~B_2(){cout<<"delete B_2\n";}
};
int test13()
{

    shared_ptr<A_2> pa= make_shared<A_2>();
    shared_ptr<B_2> pb = make_shared<B_2>();

    pa->_pb = pb;
    pb->_pa = pa;
    cout<<pa.use_count()<<endl;
    cout<<pb.use_count()<<endl;
    return 0;
}
int main()
{
    //m_string03();
    test12();
    cout<<"------------\n";
    test13();
    cout<<"------------\n";
    return 0;
}

这里写图片描述

  • 可以看出,类A和类B都是相互引用,而且都是使用shared_ptr,导致相互引用,当跳出函数时,pa和pb析构的时候,引用计数都会减1,但是引用计数还是1,导致跳出函数的时候,资源都没有被释放,(A,B析构函数都没有调用)。
  • 反之,在类A_2,类B_2中,其中B_2类中是成员_pa是weak_ptr。所以一开始,pa的引用计数是1,pb的引用计数是2。当先析构pb的时候,pb的引用计数减1,变成1,这个时候pb还没有被释放;当轮到析构pa的时候,pa的引用计数减1,变成0,这个时候pa被释放同时也会使pb的引用计数再次减1,这个时候pb的引用计数也变成了0,所以pb也会被释放。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值