【C++】智能指针用法详解(非常实用)

简介: 【C++】智能指针用法详解(非常实用)

一、指针与智能指针的区别


1、指针


 C 语言规定所有变量在使用前必须先定义,指定其类型,并按此分配内存单元。指针变量不同于整型变量和其他类型的变量,它是专门用来存放地址的,所以必须将它定义为“指针类型”。


2、智能指针


如果在程序中使用 new 从堆(自由存储区)分配内存,等到不需要时,应使用 delete 将其释放。 C++ 引用了智能指针 auto_ptr,以帮助自动完成这个过程。随后的编程体验(尤其是使用STL)表明,需要有更精致的机制。基于程序员的编程体验和 BOOST 库提供的解决方案,C++11摒弃了 auto_ptr,并新增了三种智能指针:unique_ptr、shared_ptr 和 weak_ptr。所有新增的智能指针都能与 STL 容器和移动语义协同工作。


3、智能指针和普通指针的区别


 智能指针和普通指针的区别在于智能指针实际上是对普通指针加了一层封装机制,区别是它负责自动释放所指的对象,这样的一层封装机制的目的是为了使得智能指针可以方便的管理一个对象的生命期。


二、智能指针


1、智能指针的种类


 C++中的智能指针有4种,分别为:shared_ptrunique_ptrweak_ptrauto_ptr,其中 auto_ptr被C++11弃用。


2、使用智能指针的原因


申请的空间(即new出来的空间),在使用结束时,需要 delete 掉,否则会形成内存碎片。在程序运行期间,new 出来的对象,在析构函数中 delete 掉,但是这种方法不能解决所有问题,因为有时候 new 发生在某个全局函数里面,该方法会给程序员造成精神负担。此时,智能指针就派上了用场。使用智能指针可以很大程度上避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。所以,智能指针的作用原理就是在函数结束时自动释放内存空间,避免了手动释放内存空间。


3、智能指针的使用


(1)autoi_ptr(C++98 的方案,C++11已经弃用)


 auto_ptr 智能指针采用所有权模式;

auto_ptr<string> p1(new string("I reigned loney as a cloud.")); 
auto_ptr<string> p2; 
p2=p1; //auto_ptr不会报错

说明:此时不会报错,p2 剥夺了 p1 的所有权(相当于是两个指针同时指向一块内存),但是当程序运行时访问 p1 将会报错。所以auto_ptr 的缺点是:存在潜在的内存崩溃问题。


(2)unique_ptr(替换 auto_ptr)


      unique_ptr 实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对 象。它对于避免资源泄露,例如,以 new 创建对象后因为发生异常而忘记调用 delete 时的情形特别 有用。


采用所有权模式,和上面例子一样。

unique_ptr<string> p3(new string("I reigned loney as a cloud.")); 
unique_ptr<string> p4; 
p4=p3; //此时会报错


 编译器认为P4=P3非法,避免了p3不再指向有效数据的问题。因此,unique_ptr比auto_ptr更安全。 另外unique_ptr还有更聪明的地方:当程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做(即源 unique_ptr 是临时右值时,源 unique_ptr 在赋值后不会出现被访问,所以编译器不会报错),比如:

unique_ptr<string> pu1(new string ("hello world")); 
unique_ptr<string> pu2; 
pu2 = pu1;                                    // #1 not allowed 
unique_ptr<string> pu3; 
pu3 = unique_ptr<string>(new string ("You")); // #2 allowed

说明:其中#1留下悬挂的 unique_ptr(pu1),这可能导致危害。而#2不会留下悬挂的 unique_ptr,因为它调用 unique_ptr 的构造函数,该构造函数创建的临时对象在其所有权让给 pu3 后就会被销 毁。这种随情况而已的行为表明,unique_ptr 优于允许两种赋值的auto_ptr 。


注意:如果确实想执行类似与#1的操作,要安全的重用这种指针,可给它赋新值。C++ 有一个标准库函数 std::move(),让你能够将一个 unique_ptr 赋给另一个。例如:

unique_ptr<string> ps1, ps2; 
ps1 = demo("hello"); 
ps2 = move(ps1); 
ps1 = demo("alexia"); 
cout << *ps2 << *ps1 << endl;


(3)shared_ptr


 shared_ptr 实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计 数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除 了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用 release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。


      shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。


shared_ptr 智能指针相关成员函数:


1)use_count:返回引用计数的个数;


2)unique:返回是否是独占所有权( use_count 为 1);


3)swap:交换两个 shared_ptr 对象(即交换所拥有的对象);


4)reset:放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少;


5)get:返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的.如 shared_ptr sp(new int(1)); sp 与 sp.get() 是等价的;


(4)weak_ptr


weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象。进行该对象的内存管理的是那个强引用的 shared_ptr。weak_ptr 只是提供了对管理对象的一个访问手 段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增 加或减少。weak_ptr 是用来解决 shared_ptr 相互引用时的死锁问题,如果说两个 shared_ptr 相互 引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引 用,不会增加对象的引用计数,和 shared_ptr 之间可以相互转化,shared_ptr 可以直接赋值给 它,它可以通过调用 lock 函数来获得 shared_ptr

class B; 
class A {
public:
    shared_ptr<B> pb_; 
    ~A() { 
        cout<<"A delete\n"; }
};
class B {
public: 
    shared_ptr<A> pa_; 
    ~B() { 
        cout<<"B delete\n"; }
};
void fun() { 
    shared_ptr<B> pb(new B()); 
    shared_ptr<A> pa(new A()); 
    pb->pa_ = pa; 
    pa->pb_ = pb; 
    cout<<pb.use_count()<<endl; 
    cout<<pa.use_count()<<endl; 
}
int main() { 
    fun(); 
    return 0; 
}
1)shared_ptr<B> pb(new B());函数退出时,B被析构,引用计数减一,但是 B 里面还引用了个 A ,此时计数为一,无 法释放资源,如果把两个 share_ptr 中一 个换成 weak_ptr 资源的引用计数为一, 析构后资源会被释放;
2)shared_ptr<A> pa(new A());
3)虚析构、纯虚析构解决解决的是父类指 针释放子类对象问题。

说明:  可以看到 fun 函数中 pa ,pb 之间互相引用,两个资源的引用计数为2,当要跳出函数时,智能指针 pa,pb 析构时两个资源引用计数会减一,但是两者引用计数还是为1,导致跳出函数时资源没有 被释放(AB的析构函数没有被调用),如果把其中一个改为 weak_ptr 就可以了,我们把类A里面 的 shared_ptr pb_; 改为 weak_ptr pb; 运行结果如下,这样的话,资源B的引用开始就只有1,当  pb 析构时,B 的计数变为 0,B 得到释放,B 释放的同时也会使A的计数减一,同时 pa析构时使A的 计数减一,那么 A 的计数为 0,A 得到释放。


注意:我们不能通过weak_ptr直接访问对象的方法,比如B对象中有一个方法print(),我们不能 这样访问,pa->pb->print(); 英文pb是一个weak_ptr,应该先把它转化为shared_ptr,如: shared_ptr p = pa->pb_.lock(); p->print();

相关文章
|
16天前
|
C++
C++(十八)Smart Pointer 智能指针简介
智能指针是C++中用于管理动态分配内存的一种机制,通过自动释放不再使用的内存来防止内存泄漏。`auto_ptr`是早期的一种实现,但已被`shared_ptr`和`weak_ptr`取代。这些智能指针基于RAII(Resource Acquisition Is Initialization)原则,即资源获取即初始化。RAII确保对象在其生命周期结束时自动释放资源。通过重载`*`和`-&gt;`运算符,可以方便地访问和操作智能指针所指向的对象。
|
16天前
|
C++
C++(九)this指针
`this`指针是系统在创建对象时默认生成的,用于指向当前对象,便于使用。其特性包括:指向当前对象,适用于所有成员函数但不适用于初始化列表;作为隐含参数传递,不影响对象大小;类型为`ClassName* const`,指向不可变。`this`的作用在于避免参数与成员变量重名,并支持多重串联调用。例如,在`Stu`类中,通过`this-&gt;name`和`this-&gt;age`明确区分局部变量与成员变量,同时支持链式调用如`s.growUp().growUp()`。
|
23天前
|
编译器 C++ 容器
【C++】String常见函数用法
【C++】String常见函数用法
14 1
|
28天前
|
存储 安全 C++
C++:指针引用普通变量适用场景
指针和引用都是C++提供的强大工具,它们在不同的场景下发挥着不可或缺的作用。了解两者的特点及适用场景,可以帮助开发者编写出更加高效、可读性更强的代码。在实际开发中,合理选择使用指针或引用是提高编程技巧的关键。
23 1
|
1月前
|
安全 NoSQL Redis
C++新特性-智能指针
C++新特性-智能指针
|
1月前
|
编译器 C++
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
|
30天前
|
存储 程序员 编译器
c++学习笔记08 内存分区、new和delete的用法
C++内存管理的学习笔记08,介绍了内存分区的概念,包括代码区、全局区、堆区和栈区,以及如何在堆区使用`new`和`delete`进行内存分配和释放。
37 0
|
30天前
|
存储 C++
c++学习笔记06 指针
C++指针的详细学习笔记06,涵盖了指针的定义、使用、内存占用、空指针和野指针的概念,以及指针与数组、函数的关系和使用技巧。
29 0
|
10天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
1月前
|
C语言
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)
【C初阶——指针5】鹏哥C语言系列文章,基本语法知识全面讲解——指针(5)