博舍

C/C++面试:什么是智能指针智能指针有什么作用分为哪几种各自有什么样的特点 智能诊断有什么用处

C/C++面试:什么是智能指针智能指针有什么作用分为哪几种各自有什么样的特点

说下你对智能指针的理解

因为C++使用内存的时候很容易出现野指针、悬空指针、内存泄露的问题。所以C++11引入了智能指针来管理内存。有四种:

auto_ptr:已经不用了unique_ptr:独占式指针,同一时刻只能有一个指针指向同一个对象shared_ptr:共享式指针,同一时刻可以有多个指针指向同一个对象weak_ptr:用来解决shared_ptr相互引用导致的死锁问题

重要提醒:回答这样就可以了,回到一大段一大段的!!!

unique_ptr能否被另一个unique_ptr拷贝呢?不能,因为它把它的拷贝构造函数private了。但是它提供了一个移动构造函数,所以可以通过std::move将指针指向的对象交给另一个unique_ptr,转交之后自己就失去了这个指针对象的所有权,除非被显示交回unique_ptr和shared_ptr的区别

(1)

unique_ptr代表的是专属所有权,不支持复制和赋值。但是可以移动shared_ptr代表的是共享所有权,shared_ptr是支持复制的和赋值以及移动的

(2)资源消耗上

unique_ptr在默认情况下和裸指针的大小是一样的。所以内存上没有任何的额外消耗,性能是最优的,我们大多数场景下用到的应该都是unique_ptr。shared_ptr的内存占用是裸指针的两倍。因为除了要管理一个裸指针外,还要维护一个引用计数。因此相比于unique_ptr,shared_ptr的内存占用更高。在使用shared_ptr之前应该考虑,是否真的需要使用shared_ptr,而非unique_ptr。unique_ptr=unique_ptr和shared_ptr=shared_ptr这两个操作有什么后果呢?

先来说下unique_ptr:

这个操作是不允许的,因为unique_ptr它的原理是将拷贝构造和拷贝赋值私有化,但是它提供了移动构造和移动赋值。所以如果你想要使用=赋值,必须先把右边的用std::move包裹一下,这样右边的unique_ptr就会失去所有权,左边的unique_ptr就会得到对应对象的所有权

至于shared_ptr:

对于左边的指针,它会将自己的引用计数减一,然后检测一下是不是减到了0,如果是,那么delete所管理的对象然后将右边的引用计数和管理对象赋值给左边,此时两边指向同一个对象,共享同一个引用计数,然后引用计数++shared_ptr的移动赋值时发生了什么事情首先它会检查本指针和参数指针是不是同一个对象,如果是,直接返回然后,先把本指针的引用变量–,如果发现减到了0,就把参数指针和参数引用变量析构掉并置NULL最后,本指针和参数指针指向同一个对象以及引用计数,然后引用计数自增1shared_ptr什么时候会被释放掉,就是什么时候引用次数为0?在一个shared_ptr析构时,或者被赋值时,强引用计数会减1。减到0就delete资源shared_ptr你是怎么实现的

shared_ptr内部是利用引用计数来实现内存的自动管理,每当复制一个shared_ptr,引用计数会+1。当一个shared_ptr离开作用域时,引用计数会-1。当引用计数为0的时候,则delete内存。

关键在于多个对象持有同一个引用对象,第一次创建指针的时候可以new一个int,然后大家都有一个int指针就可以共用了

shared_ptr应该要有三个成员:

裸指针:指向所要管理的对象强引用计数:就是一个int指针,记录了有多少个shared_ptr指向裸指针弱引用计数:也是一个int指针,记录了有多少个weak_ptr指向裸指针shared_ptr是不是线程安全不是引用计数的增减是原子操作没问题,但是shared_pytr的读写本身不只包括引用计数操作,还包括资源所有权的操作,这两个操作合起来不是原子的如果要求线程安全必须加锁sharedPtr在64位操作系统下大小有多大两个指针,64字节shared_ptr和weak_ptr之间是什么关系?weak_ptr是用来辅助shared_ptr的,每一个weak_ptr它指向weak_ptr而不是实际的操作函数为什么推荐用makeshared创建指针

优点是:

它分配的时候,只分配一次,而shared_ptr的构造函数需要分配两次。效率更高更安全缺点是:如果有weak_ptr不推荐使用,否则会出现知道最后一个weak_ptr被释放了才真正去释放管理的对象为什么要用shared_from_this?

我们往往会需要在类内部使用自身的shared_ptr,例如:

classWidget{public:voiddo_something(A&a){a.widget=该对象的shared_ptr;}}

我们需要把当前shared_ptr对象同时交由对象a进行管理。意味着,当前对象的生命周期的结束不能早于对象a。因为对象a在析构之前还是有可能会使用到a.widget。

如果我们直接a.widget=this;,那肯定不行,因为这样并没有增加当前shared_ptr的引用计数。shared_ptr还是有可能早于对象a释放。

如果我们使用a.widget=std::make_shared(this);,肯定也不行,因为这个新创建的shared_ptr,跟当前对象的shared_ptr毫无关系。当前对象的shared_ptr生命周期结束后,依然会释放掉当前内存,那么之后a.widget依然是不合法的。

对于这种,需要在对象内部获取该对象自身的shared_ptr,那么该类必须继承std::enable_shared_from_this。代码如下:

classWidget:publicstd::enable_shared_from_this{public:voiddo_something(A&a){a.widget=shared_from_this();}}

这样才是合法的做法。

weak_ptr的原理?shared_ptr中有一个成员时,弱引用计数。往weak_ptr被赋值时,弱引用计数自增1weak_ptr的使用场景用来解决悬空指针问题。通过std::shared_ptr管理数据并将std::weak_ptr提供给数据用户,用户可以通过expired()或者lock()来检测数据的有效性打破shared_ptr相互引用导致死锁的问题。方法:将任意一个改为weak_ptr有时候我们需要“如果对象还活着,就调用它的成员函数,否则忽略之”的语意缓存对象关于shared_ptr相互引用具体讲一下

weak_ptr是为了解决shared_ptr双向引用的问题。即:

classB;structA{shared_ptrb;};structB{shared_ptra;};autopa=make_shared();autopb=make_shared();pa->b=pb;pb->a=pa;

pa和pb存在着循环引用,根据shared_ptr引用计数的原理,pa和pb都无法被正常的释放。对于这种情况,我们可以使用weak_ptr:

classB;structA{shared_ptrb;};structB{weak_ptra;};autopa=make_shared();autopb=make_shared();pa->b=pb;pb->a=pa;

weak_ptr不会增加引用计数,因此可以打破shared_ptr的循环引用。通常做法是parent类持有child的shared_ptr,child持有指向parent的weak_ptr。这样也更符合语义。

weak_ptr如何检测指针是否被销毁

expired():

判断强引用计数是否为0如果返回true,那么被观测的对象(也就是shared_ptr管理的资源)已经不存在了如何将weak_ptr转换为shared_ptr

用lock():

如果expired()为true,返回一个空shared_ptr,否则返回非空shared_ptr。lambda有哪几种捕获方式

用lambda捕获uniquePtr应该怎么做

引用捕获,因为uniquePtr没有拷贝构造

我们在lambda表达式中使用的是按值传递;按值传递就会产生副本,就会产生一个unique_ptr的copy;但是我们知道的,这显然是错误。解决方法很简单,就是按引用传递替代按指针传递:autostr=std::make_unique("mystring");autolambda=[capturedStr=std::move(str)]{std::coutreturnunique_ptr(newstring("goodluck"));//这是一个右值(临时对象都是右值)}voidtest18(){shared_ptrps1=myfunc();//可以unique_ptrpi1(newint(1));shared_ptrpi2=std::move(pi1);//可以,移动后pi1被置空//unique_ptrpi3(newint(100));//shared_ptrpi4(pi3);//不可以,原因pi4的参数不是右值}

ps:不要啰啰嗦嗦,尽量回答精简

待整理

因为C++的内存管理很容易出现问题:

野指针:一些内存单元已经释放,但是之前指向它的指针还在被使用重复释放:试图去释放已经释放的内存泄露:应该释放的没有被释放

所以C++引入了智能指针。智能指针可以让对象退出作用域时,自动delete构造函数

智能指针是一个RAII类模板,用于动态分配内存,其设计思想是将基本类型指针封装为(模板)类对象指针,并在离开作用域时调用析构函数,使用delete删除指针所执行的内存空间。

分为auto_ptr、unique_ptr、shared_ptr和weak_ptr四种,各自的特点:

auto_ptr,实现了独占式拥有的概念,同一时间只能由一个只能指针可以指向该对象;但auto_ptr已经被C++11放弃,其主要问题在于:对象所有权的转移,比如在函数传参的过程中,对象所有权不会返还,从而存在潜在的内存崩溃的问题不能指向数组,也不能作为STL容器的成员unique_ptr:独占式指针,与所指对象的内存绑定紧密,禁止其他智能指针与其他共享同一个对象。也就是同一时间只能有一个智能指针可以指向该对象独占的意思是不可以复制(拷贝构造和拷贝复制),但是我们可以利用std::move将其转移给其他unique_ptr(可以移动构造和移动赋值)。一旦转移,这个所有权就会失去,除非被显示归还从实现上来讲,unique_ptr是一个删除了拷贝构造函数,保留了移动构造函数的指针类型。可以使用右值对unique_ptr进行构造shared_ptr:实现了共享式拥有的概念,即多个智能指针可以指向相同的对象,该对象以及相关资源会在其所指对象不再使用之后,自动释放与对象相关的资源它是使用计数机制来表明资源被几个指针共享可以通过成员函数use_count()来查看资源的所有者个数,除了可以通过new来构造,还可以通过传⼊auto_ptr,unique_ptr,weak_ptr来构造。当我们调⽤release()时,当前指针会释放资源所有权,计数减⼀。当计数等于0时,资源会被释放。shared_ptr是为了解决auto_ptr在对象所有权上的局限性(auto_ptr是独占的),在使⽤引⽤计数的机制上提供了可以共享所有权的智能指针。因为使用shard_ptr仍然需要new来调用,这使得代码出现了不对称,std::make_shared_ptr就可以显示消除这个newweak_ptr,解决shared_ptr相互引用的问题,两个指针的引用计数永远不会下降为0,从而导致死锁问题。而weak_ptr是对对象的一种弱引用,可以绑定到shared_ptr,但不会增加对象的引用计数。weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能智能,因为它没有普通指针的行为,没有重置*和→。它得最大作用是协助shared_ptr工作,像是旁观者那样观测资源的使用情况weak_ptr是一种不控制对象生命周期的智能指针,它指向一个shared_ptr管理的对象。进行该对象的内存管理的是那个强引用的shared_ptrweak_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。当两个智能指针都是shared_ptr类型的时候,析构时两个资源引⽤计数会减⼀,但是两者引⽤计数还是为1,导致跳出函数时资源没有被释放(的析构函数没有被调⽤),解决办法:把其中⼀个改为weak_ptr就可以。weak_ptr可以从一个shared_ptr和另一个weak_ptr对象构造,它是一种弱引用(shared_ptr是一种强引用),弱引用不会引起引用计数的增加,它提供了一个expired()方法用来判断当前weak_ptr是否为空或者执行的堆内存是否已经被释放了。提供了一个lock(),如果当前weak_ptr已经过期,那么就会返回一个空的shared_ptr指针,否则,会返回实际的shared_ptr指针shared_ptrVSunique_ptr尽可能用unique_ptrshared_ptr应该仅在绝对需要共享时使用避免使用任何其他指针,如weakptr、raw指针等。

从技术上讲,所有unique_ptr都可以被shared_ptr替换而不会出现编译错误,但我们仍然使用unique_ptr,原因如下:

unique_ptr具有与C++原始指针“几乎”相同的效率,包括大小分配、引用/取消引用(假设没有自定义构造函数)。unique_ptr明确了对象的所有权和生存时间。shared_ptr的大小是原始指针的两倍,堆栈更大。最重要的是,shared_ptr需要维护引用计数,并且该操作必须是原子的(即线程安全的),这与unique_ptr相比增加了恒定的开销。

因此,我们不应该仅仅因为方便而使用shared_ptr:对于shared_pcr的每次使用,我们都需要明确解释为什么这应该是一个共享指针,以及需要共享这个对象的模块/函数是什么。

避免使用所有其他指针,如weak_ptr或raw指针,因为在大多数情况下,它们可以被unique_ptr/shared_ptr替换。然而,显然也有例外,例如,所有QT应用程序都建议使用原始指针来管理UI组件。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。

上一篇

下一篇