C++13(智能指针)

智能指针

什么是智能指针

​ 智能指针其实是⼀个类,实质就是重载了->和*操作符的类,由类来实现对内存的管理,确保即使有异常产⽣,也可以通过智能指针类的析构函数完成内存的释放。智能指针的⾏为类似常规指针,重要的区别在于智能指针负责⾃动释放所指向的对象。

STL提供给我们提供了四种智能指针类型,除了下边列出的三种,还有C++98标准auto_ptr智能指针,C+11已将将其摒弃。C++11 标准新引⼊了 这 3 个智能指针:

shared_ptr : 允许多个智能指针指向同⼀个对象

unique_ptr : ⼀个指针“独占”所指向的对象

weak_ptr : 弱引⽤,指向shared_ptr所管理的对象

unique_ptr

unique_ptr它提供了对动态分配内存的独占所有权的管理。unique_ptr确保同⼀时间只有⼀个unique_ptr实例可以拥有⼀个对象的指针,从⽽避免了资源的多重释放问题。当 std::unique_ptr 被销毁时,它所指向的对象也会被⾃动销毁。unique_ptr 智能指针是以模板类的形式提供的,unique_ptr<T>(T 为指针所指数据的类型),定义在<memory>头⽂件,并位于 std 命名空间中。

特点:

● 轻量级:没有额外的引⽤计数开销,因此效率较⾼。

● 不可复制: std::unique_ptr 不能被复制,但可以通过 std::move 转移

所有权。

● ⾃动释放: 当 unique_ptr 离开作⽤域时,它所管理的对象会被⾃动删除。

基础操作如下:

1.创建unique_ptr对象

#include <iostream>
#include<string>
#include<memory>
using namespace std;
class stu
{
    public:
    string name;
    int age;
    stu(string name, int age)
    {
        this->name = name;
        this->age = age;
    }
    void test()
    {
        cout << "fron stu method test" << endl;
    }
    ~stu()
    {
        cout << "stu 析构函数" << endl;
    }
};
//1.创建空智能指针对象
unique_ptr<int> ptr; //构建⼀个空的唯⼀智能指针对象
std::unique_ptr<int> p2(nullptr);
//2.初始化智能指针对象 
//unique_ptr<int>myptr(new int);//创建⼀个⾮空的智能指针对象,由该对象实现托管对象
的内存释放
    unique_ptr<int>myptr(new int(12));
unique_ptr<stu>ptr1(new stu("yq", 12));//构建⼀个智能指针对象指向⽤户新建的堆对
象。

2.访问所指向对象

通过解引⽤运算符 * 可以访问 std::unique_ptr 所指向的对象。或使⽤箭头运算符 -> 进⾏指向对象成员的访问

int* pt = new int(3); //
unique_ptr<int> myptr(pt);
//通过智能指针对绑定的单个对象进⾏操作 假如你绑定的stu类型 此时访问成员: myptr->age
cout << *myptr << endl; 
*myptr = *myptr + 100;//修改所指向对象
//通过指向运算符对托管对象的成员进⾏访问
unique_ptr<stu> ptr(new stu("yq", 12));
cout << ptr->age << endl;
cout << ptr->name << endl;
3.智能指针绑定动态数组

声明形式:unique_ptr<T[ ]> up(p)

unique_prt< int[ ] > up( new int[10] );
for( size_t i = 0 ; i ! = 10 ; ++ i )
    up[ i ]=i; //对其初始化 //cout<<up[ i ] <<endl; //
//或者绑定时直接初始化
unique_ptr<int[]> p(new int[3]{11,22,33});
4.转移 unique_ptr 对象的所有权

不能对unique_ptr 对象进⾏拷⻉,下列语句⽆法执⾏

unique_ptr taskPtr2 = taskPtr; // ⾮法

但是可以移动它,所谓“移动”,其实就是转移所有权,应该使⽤std::move;

注意,两个unique_ptr不能指向同⼀个内存地址。

unique_ptr<Task> p1(new Task(23));
unique_ptr<Task>p2;
//p2 = p1或者unique_ptr<Task>p2(p1);错的 unique_ptr对象不能拷⻉,但是可以移
动
    p2 = move(p1);
cout << p2->mId << endl; //23
将与 p1 绑定的 raw 指针转移给 p2,在这之后,p1 变为空
    //检查 unique_ptr 对象是否为空
    if (ptr1 == nullptr) //或者if(!ptr1)
        cout << "指针为空" << endl;
else
    cout << "指针不为空" << endl;
5.释放关联的原始指针

unique_ptr 的 release() 函数可以直接将对绑定 raw 指针的所有权释放,该函数会将绑定的 raw 指针返回,注意,这⾥的释放所有权,并没有delete原始指针,⽽reset()会delete原始指针。调⽤ unique_ptr 的 reset() ⽅法将删除与之绑定的 raw 指针,并且将 unique_ptr对象置空.

unique_ptr<Task> p1(new Task(23));
Task *p2=p1.release(); //p1 变为空,并且其绑定的 raw 指针被赋值给 p2。
if(p1 == nullptr)
    std::cout << "p1 is empty" << std::endl;
cout << p2->mId << endl; //23
// 此时需要⼿动删除⽤完的指针
delete p2;

当指针指针对象超出作⽤域时会⾃动析构,unique_ptr对象的析构函数中会delete其关联指针,这样就相当于替我们执⾏了delete堆内存上的对象。

6.get()⽅法仅仅返回关联指针,但不会释放所有权
int* p = new int;
unique_ptr<int> ptr1(p);
cout << ptr1 <<":" <<p<<"#"<<ptr1.get() << endl;
>> 016BB98:0016BB98#0016BB98

shared_ptr

当多个对象需要共享同⼀个资源,并且在所有使⽤该资源的对象都不再需要它时,资源才应该被释放,这时 std::shared_ptr 就⾮常有⽤。

shared_ptr基于”引⽤计数”模型实现,多个shared_ptr可指向同⼀个动态对象,并维护⼀个共享的引⽤计数器,记录了引⽤同⼀对象的shared_ptr实例的数量。当最后⼀个指向动态对象的shared_ptr销毁时,会⾃动销毁其所指对象(通过delete操作符)。可共享指针对象,可以赋值给shared_ptr或weak_ptr。

3)shared_ptr

所属头⽂件:#include

所属命名空间及标识符:using std::shared_ptr;

所属版本:C++11

g++启⽤版本命令:g+±std=c++11-c -o

  • 定义智能指针对象
std::shared_ptr<int> p1; //不传⼊任何实参
std::shared_ptr<int> p2(nullptr); //传⼊空指针 nullptr
注意,空的 shared_ptr 指针,其初始引⽤计数为 0,⽽不是 1
  • 初始化shared_ptr类对象

可以使⽤make_shared在动态内存中分配⼀个对象并初始化它, 返回指向此对象的shared_ptr。除此之外还可以通过构造函数、赋值函数初始化智能指针。

格式:shared_ptr<类型> 指针名 = make_shared<类型>();

//⽅式1:⽤具体的值初始化智能指针对象指向内存
shared_ptr<int> my1(new int(11));//通过具体的值直接初始化共享智能指针
//⽅式2 :⽤另⼀个智能指针对象初始化智当前指针对象
shared_ptr<int> p2=p1;
cout<<*p1<<endl;
//⽅式3: make_shared时,必须指定想要创建的对象的类型,定义⽅式与模板类相同
shared_ptr<int> iptr = make_ptr<int>(); // 使⽤int的默认构造函数,初始化shared
_ptr对象的内容
    shared_ptr<int> iptr = make_ptr<int>(36); // 使⽤int的重载构造函数,初始化shar
ed_ptr对象的内容为36

make_shared的标准库的模板函数,推荐这种⽅式初始化指针对象,因为使⽤std::make_shared能够减⼩了内存分配的次数,提升效率。

可以通过成员函数use_count()来查看资源的所有者个数。

//get⽅法可以获取智能指针绑定对象的地址

shared_ptr<Task> p1(new Task(123));//调⽤构造函数输出Test create
shared_ptr<Task> p2(new Task(456));//调⽤构造函数输出 Test creat
cout <<p1->mId << endl; //输出123
cout << p2 <<":"<< p2.get() << endl;//返回保存的指针
cout << p2.use_count() << endl;//显示此时资源被⼏个指针共享,输出1
shared_ptr<Task> p3 = p2; //增加⼀个引⽤
cout << p2.use_count() << endl;
//重新绑定对象,绑定⼀个空对象,当时此时指针指向的对象还有其他指针能指向就不会释放该对象
的内存空间
p2.reset();
cout << p3.use_count() << endl;
cout << p3->mId << endl; //456
>>>
Task::Constructor
Task::Constructor
123
00BFBEB8:00BFBEB8
1
2
1
456
Task::Destructor
Task::Destructor

递增的情况有三种(这⾥类似复制构造函数,存在的情形):

a.⽤⼀个shared_ptr初始化另外⼀个shared_ptr

b.作为函数参数传参(拷⻉实参对象)

c.作为函数返回值(返回出来还是要赋值)

● 访问绑定对象的值或成员通过*或->

shared_ptr<stu> share(new stu("yq", 33));
cout << share->name <<":"<< (*share).name<< endl;

共享指针绑定到动态数组

//将共享智能绑定到动态数组
shared_ptr<int[]>ss(new int[3]{ 11,22,33 });
for (int i = 0; i < 3; i++)
{
    cout << ss[i]<< endl;
}

shared_ptr使⽤注意:

1)同⼀普通指针不能同时为多个 shared_ptr 对象初始化,否则会导致程序发⽣异

常(原因析构是会出问题)

2)不能⽤ delete get()返回的指针

3)不使⽤get()初始化或reset另⼀个智能指针

3)如果你使⽤get()返回的指针,记住当最后⼀个对应的智能指针销毁后,你的指针就变为⽆效了。

4)weak_ptr

std::weak_ptr 是 C++ 标准库 头⽂件中提供的⼀种智能指针,它是⼀种弱引⽤,⽤于配合 std::shared_ptr 使⽤,主要⽤于解决 std::shared_ptr 可能存在的循环引⽤问题,避免内存泄漏。以下是 std::weak_ptr的⼀些常⻅使⽤场景:

#include <iostream>
#include <memory>
class B;
class A {
    public:
    std::weak_ptr<B> bPtr;
    ~A() { std::cout << "A destroyed" << std::endl; }
};
class B {
    public:
    std::shared_ptr<A> aPtr;
    ~B() { std::cout << "B destroyed" << std::endl; }
};
int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();
    a->bPtr = b;
    b->aPtr = a;
    return 0;
}

当两个或多个对象通过 std::shared_ptr 相互引⽤,形成循环引⽤时,会导致这些对象的引⽤计数永远不会变为 0,从⽽造成内存泄漏。 std::weak_ptr 可以打破这种循环引⽤,因为它不会增加对象的引⽤计数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值