智能指针_

智能指针是类模版,在栈上创建智能指针。

把普通指针交给智能指针对象

智能指针对象过期时,调用析构函数释放普通指针的内存。

c++11的主流三种智能指针:

1、unique_ptr

unique_ptr独享它指向的对象,也就是说,同时只有一个unique_ptr指向同一对象,当这个unique_ptr被销毁时,指向的对象也随即被销毁。

包含头文件#include <memory>

AA* p = new AA("西施");

unique_ptr<AA> pu1(p);

AA是一个类,当使用unique_ptr时,将应用pu1管理p指针

使用unique_ptr有三种方法:

方法一:

unique_ptr<AA> p0(new AA("西施")); //分配内存并初始化

方法二:

unique_ptr<AA> p0 = make_unique<AA> ("西施"); //c++14标准

方法三:

AA* p = new AA("西施");

unique_ptr<AA> pu1(p); //用已存在的地址初始化,这种方法不太好,但易于理解

违法操作:

1、

AA* p = new AA("西施");

unique_ptr<AA> pu1 = p;  //错误,不能把普通指针直接赋值给智能指针

unique_ptr<AA> pu3 = new AA("西施"); //错误,不能把普通指针直接给智能指针

unique_ptr<AA> pu2 = pu1; //错误,unique_ptr不能进行拷贝

unique_ptr<AA> pu3;

pu3 = pu1; //这个也是违法的,错误,不能用=对unique_ptr进行赋值

2、不要用同一裸指针初始化多个unique_ptr对象

3、get()方法返回裸指针

4、不要用unique_ptr管理不是new分配的内存

具体unique_ptr指针指向分析:

AA* p = new AA("西施");

unique_ptr<AA> pu1(p);

cout << "裸指针的值是:" << p <<endl; //000002B970F36070

cout << "pu1输出的结果是:" << pu1 <<endl; //000002B970F36070

cout << "pu1.get()输出的结果是:" << pu1.get() <<endl; //000002B970F36070

cout << "pu1的地址是:" << &pu1 <<endl; //00000D22257FAA8

所以p(裸指针)、pu1、pu1.get()指向的是一样的,而&pu1是存放裸指针的地址。

用于函数的参数

传引用(不能传值,因为unique_ptr没有拷贝构造函数)。

裸指针

不支持指针的运算

二、更多技巧

1、将一个unique_ptr赋给另一个时,如果源unique_ptr是一个临时右值,编译器允许这么做;如果源unique_ptr将存在一段时间,编译器禁止这么做。一般用于函数的返回值。

举例:

unique_ptr<AA> func(){

        unique_ptr<AA> pp(new AA("西施3"));

        retrn pp; // 

}

int main()

{

        unique_ptr<AA> pu1(new AA("西施1"));

        unique_ptr<AA> pu2;

        pu2 = pu1; //错误

        pu2 = unique_ptr<AA> (new AA("西施1")); //可行,用匿名对象赋值

        pu2 = func(); //可行,用函数的返回值赋值


}

2、用空指针nullptr给unique_ptr赋值将释放对象,空的unique_ptr == nullptr;

3、release()释放对原指针的控制权,将unique_ptr置为空,返回裸指针。

4、std::move()可以转移对原始指针的控制权。(可用于把unique_ptr传递给子函数,子函数形参也是unique_ptr)

举例:

void func1 (const AA *a){

        只读

}

void func2 ( AA *a){

        delete a;

}

void func3 (const unique_ptr<AA> &a)

void func4 (unique_ptr<AA> a)

int main{

         unique_ptr<AA> pu(new AA("西施"));

        func1(pu.get());

        func2(pu.release());

        func1(pu);

        func1(move(pu));

}

5、reset()释放对象

6、swap()交换两个unique_ptr的控制权

7、unique_ptr也可像普通指针那样,当指向一个类继承体系时,也具有多态性质

8、unique_ptr不是绝对安全的,如果程序中调用exit()退出,全局的unique_ptr可以自动释放,但局部的unique_ptr无法释放

9、unique_ptr提供了支持数组的具体化版本

智能指针shared_ptr

介绍:shared_ptr共享它指向的对象,多个shared_ptr可以指向(关联)相同的对象,在内部采用计数机制来实现。

        当新的shared_ptr与对象关联时,引用计数增加1。

        当shared_ptr超出作用域时,引用技术减1。当引用技术变为0时,则表示没有任何shared_ptr与对象关联,则释放该对象。

1、基本用法

shared_ptr的构造函数也是explicit,但是没有删除拷贝构造函数和赋值函数。

1、初始化

方法一:

shared_ptr<AA> p0(new AA("西施")); //分配内存并初始化

方法二:

shared_ptr<AA> p0 = make_shared<AA> ("西施"); //c++11标准,效率更高,推荐这一种

方法三:

AA* p = new AA("西施");

shared_ptr<AA> pu1(p); //用已存在的地址初始化,这种方法不太好,但易于理解

方法四:

shared_ptr<AA> p0(new AA("西施"));

shared_ptr<AA> p1(p0);  //用已存在的shared_ptr初始化,技术加1.

shared_ptr<AA> p1 = p0; //用已存在的shared_ptr初始化,技术加1.

2、使用方法

1、智能指针重载了*和->操作符,可以像使用指针一样使用shared_ptr。

2、use_count()方法返回引用计数器的值。

3、unique()方法,如果use_.count()为1,返回true,否则返回false。

4、shared支持普通的拷贝和赋值,左值的shared_ptr的计数器将减1,右值shared_ptr的计算器将加1。

赋值时,左值的左值的shared_ptr的计数器将减1,右值shared_ptr的计算器将加1,例如:p0 = p1;

5、get()方法返回裸指针

6、不要用同一个裸指针初始化多个shared_ptr。

7、不要用shared_ptr管理不是new分配的内存。

3、用于函数的参数

与unique_ptr的原理相同

4、不支持指针的运算(+、-、++、--)

2、更多细节

1、用nullptr给shared_ptr赋值将把计数减1,如果计数为0,将释放对象,空的shared_ptr == nullptr。

2、std::move()可以转移对原始指针的控制权。还可以将unique_ptr转移成shared_ptr。

3、reset()改变与资源的关联关系。

pp.reset();  //接触与资源的关系,资源的引用计数减1.

pp.reset(new AA("bbb")); //解除与资源的关系,资源的引用计数减1。关联新资源。

4、swap()交换两个shared_ptr的控制权。

void swap(shared_ptr<T> &_Right);

5、shared_ptr也可像普通指针那样。当指向一个类继承体系的基类对象时,也具有多态性质,如同使用裸指针管理基类对象和派生类对象那样。

6、sahred_ptr不是绝对安全的,如果程序中调用exit()退出,全局的sahred_ptr可以自动释放,但局部的shared_ptr无法释放

7、shared_ptr提供了支持数组的具体化版本。

数组版本的shared_ptr,重载了操作符[],操作符[]返回的是引用,可以作为左值使用。

8、shared_ptr的线程安全性:

(1)shared_ptr的引用计数本身的线程安全(引用计数是原子操作)。

(2)多个线程同时读同一个shared_ptr对象是线程安全的。

(3)如果是多个线程对同一个shared_ptr对象进行读和写在,则需要加锁。

(4)多线程读写shared_ptr所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护。

9、如果unique_ptr能解决问题,就不要用shared_ptr。unique_ptr的效率更高,占用的资源更少。

智能指针weak_ptr

shared_ptr内部维护了一个共享的引用计数器,多个shared_ptr可以指向同一资源。如果出现了循环引用的情况,引用计数永远无法归0,资源不会被释放。

weak_ptr是什么

weak_ptr是为了配合shared_ptr而引入的,它指向一个由shared_ptr管理的资源但不影响资源的生命周期。也就是说,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。

不论是否有weak_ptr指向,如果最后一个指向资源的shared_ptr被销毁,资源就会被释放。

weak_ptr更像是shared_ptr的助手而不是智能指针。

weak_ptr没有重载->和*操作符,不能直接访问资源。

有以下成员函数:

1)operator = (); //把shared_ptr或weak_ptr赋值给weak_ptr。

2)expired();     //判断它指资源是否已过期

3)lock(); //返回shared_ptr,如果资源已过期,返回空的shared_ptr。

4)reset(); //将当前weak_ptr指针置空。

5)swap(); //交换

weak_ptr不控制对象的生命周期,但是,它知道对象是否还活着。

用lock()函数把它可以提升为shared_ptr,如果对象还活着,返回有效的shared_ptr,如果对象已经死了,提升会失败,返回一个空的shared_ptr。

提升的行为(lock())是线程安全的。

所以:因为lock()是原子操作,不会被其他线程占用

shared_ptr<BB> pp = pa->m_p.lock(); //把weak_ptr提升为shared_ptr

if (pp == nullptr)

        cout << "语句块内部:pa->m_p已过期。\n";

else

        cout << "语句块内部:pp->m_name=" << pa->m_p.lock() -> m_name() << endl;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值