C++智能指针

C++智能指针前言 C 标准模板库 STL Standard Template Library 一共给我们提供了四种智能指针 auto ptr unique ptr shared ptr 和 weak ptr 其中 auto ptr 是 C 98 提出的 C 11

大家好,我是讯享网,很高兴认识大家。

前言

C++ 标准模板库 STL(Standard Template Library) 一共给我们提供了四种智能指针:auto_ptr、unique_ptr、shared_ptr 和 weak_ptr,其中 auto_ptr 是 C++98 提出的,C++11 已将其摒弃,并提出了 unique_ptr 替代 auto_ptr。虽然 auto_ptr 已被摒弃,但在实际项目中仍可使用,但建议使用更加安全的 unique_ptr,后文会详细叙述。shared_ptr 和 weak_ptr 则是 C+11 从准标准库 Boost 中引入的两种智能指针。此外,Boost 库还提出了 boost::scoped_ptr、boost::scoped_array、boost::intrusive_ptr 等智能指针,虽然尚未得到 C++ 标准采纳,但是在开发实践中可以使用。

一 auto_ptr

auto_ptr 同样是 STL 智能指针家族的成员之一,由 C++98 引入,定义在头文件。其功能和用法类似于 unique_ptr,由 new expression 获得对象,在 auto_ptr 对象销毁时,他所管理的对象也会自动被 delete 掉。

下面是一些成员函数及其简介

  1. get:得到对象类管理的对象指针
  2. operator*:得到管理的对象
  3. operator->:等价于(*class).,用于获取对象的某个成员
  4. release:将管理对象的指针返回并设置类内指针为null
  5. operator= :相当于调用release之后将返回值构造为新的类初始化左边的类,Release and copy auto_ptr
  6. reset:若无参数直接释放当前对象否则用参数重新设置当前值,无返回值

下面是一个小例子

#include <iostream> #include <memory> int main() { 
    /* get:得到对象类管理的对象指针 operator*:得到管理的对象 operator->:等价于(*class).,用于获取对象的某个成员 release:将管理对象的指针返回并设置类内指针为null operator= :相当于调用release之后将返回值构造为新的类初始化左边的类,Release and copy auto_ptr reset:若无参数直接释放当前对象否则用参数重新设置当前值,无返回值 */ std::auto_ptr<int> a; a.reset(new int);//reset:当前对象否则用参数重新设置当前值,无返回值 *a = 1; cout << "a:" << *a << endl;// operator*:得到管理的对象 *a = 2; std::auto_ptr<int> b; b = a;//operator= :相当于调用release之后将返回值构造为新的类初始化左边的类, if (a.get())//get:得到对象类管理的对象指针  { 
    cout << "a:" << *a << endl; } cout << "b:" << *b << endl; *b = 3; int *c = b.release();//release:将管理对象的指针返回并设置类内指针为null if (a.get()) { 
    cout << "b:" << *b << endl; } *c = 4; cout << "c:" << *c << endl; std::auto_ptr<int> d; d.reset(c); *d = 5; if (d.get()) { 
    cout << "d:" << *d << endl; } cout << "c:" << *c << endl; d.reset();//reset:若无参数直接释放,无返回值 if (d.get()) { 
    cout << "d:" << *d << endl; } getchar(); return 0; } 

讯享网

二.unique_ptr

unique_ptr 由 C++11 引入,旨在替代不安全的 auto_ptr。unique_ptr 是一种定义在头文件中的智能指针。它持有对对象的独有权——两个unique_ptr 不能指向一个对象,即 unique_ptr 不共享它所管理的对象。它无法复制到其他 unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库 (STL)算法。只能移动 unique_ptr,即对资源管理权限可以实现转移。这意味着,内存资源所有权可以转移到另一个 unique_ptr,并且原始 unique_ptr 不再拥有此资源。实际使用中,建议将对象限制为由一个所有者所有,因为多个所有权会使程序逻辑变得复杂。因此,当需要智能指针用于存 C++ 对象时,可使用 unique_ptr,构造 unique_ptr 时,可使用 make_unique Helper 函数。

相比于auto_ptr,其可以在编译期间合理确定智能指针对象作为右值时的合法性,只有作为临时右值才合法否则报错

讯享网unique_ptr<int> getUnique(int parm) { 
    unique_ptr<int> temp(new int(parm)); return temp; } int main() { 
    unique_ptr<int> a; //创建空智能指针 a.reset(new int); //绑定动态对象  *a = 1; cout << "a:" << *a << endl; *a = 2; unique_ptr<int>b = std::move(a);//所有权转移(通过移动语义),u_s所有权转移后,变成“空指针”  if (a.get()) { 
    cout << "a:" << *a << endl; } cout << "b:" << *b << endl; *b = 3; unique_ptr<int> c; c.reset(b.release());//所有权转移 if (b.get()) { 
    cout << "b:" << *b << endl; } cout << "c:" << *c << endl; //unique_ptr<int> d = c; //编译出错,已禁止拷贝 //unique_ptr<int> d(c); //编译出错,已禁止拷贝 //unique_ptr<int> d; //d = c;//编译出错,已禁止赋值 unique_ptr<int> d = getUnique(1);//临时正确 getchar(); return 0; } 

三 shared_ptr

shared_ptr 是一个标准的共享所有权的智能指针,允许多个指针指向同一个对象,定义在 memory 文件中,命名空间为 std。shared_ptr最初实现于Boost库中,后由 C++11 引入到 C++ STL。shared_ptr 利用引用计数的方式实现了对所管理的对象的所有权的分享,即允许多个 shared_ptr 共同管理同一个对象。像 shared_ptr 这种智能指针,《Effective C++》称之为“引用计数型智能指针”(reference-counting smart pointer,RCSP)。

shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针,当然这需要额外的开销:
(1)shared_ptr 对象除了包括一个所拥有对象的指针外,还必须包括一个引用计数代理对象的指针;
(2)时间上的开销主要在初始化和拷贝操作上, * 和 -> 操作符重载的开销跟 auto_ptr 是一样;
(3)开销并不是我们不使用 shared_ptr 的理由,,永远不要进行不成熟的优化,直到性能分析器告诉你这一点。

下面是一些成员函数及其简介

  1. swap:交换对象的指针
  2. reset:重置指针
  3. get:返回保存的指针
  4. operator*:解引用,得到对象
  5. use_count:引用计数
  6. unique:当前引用技术是否为1
  7. operator bool:检查是否为空

shared_ptr 没有release函数。

make_shared函数:
安全的分配和使用动态内存的方法就是调用一个名为make_shared的标准库函数,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。头文件和share_ptr相同,在memory中


讯享网

int main() { 
    shared_ptr<int> a = make_shared<int>(1); std::shared_ptr<int> b(new int(2)); cout << "a:" << *a << endl; cout << "b:" << *b << endl; a.swap(b);//交换a和b cout << "a:" << *a << endl; cout << "b:" << *b << endl; cout << "a:Count:" << a.use_count() << endl; cout << "b:Count:" << b.use_count() << endl; std::shared_ptr<int> c = a;//赋值 cout << "c:" << *c << endl; cout << "a:Count:" << a.use_count() << endl; cout << "b:Count:" << b.use_count() << endl; cout << "c:Count:" << c.use_count() << endl; *b = 3; if (b.unique())//引用技术是否为1 { 
    cout << "b:" << *b << endl; } a.reset();//a 的引用计数为0 但是c还是可以使用 cout << "a:Count:" << a.use_count() << endl; cout << "b:Count:" << b.use_count() << endl; cout << "c:Count:" << c.use_count() << endl; a.reset(new int(4)); cout << "a:Count:" << a.use_count() << endl; cout << "b:Count:" << b.use_count() << endl; cout << "c:Count:" << c.use_count() << endl; getchar(); return 0; } 

实现自己的智能指针

讯享网template <typename T> class Smarter; //辅助类 template <typename T> class Assistant { 
    private://全部私有 friend class Smarter<T>;//智能指针为友元,可以访问私有成员 Assistant(T* p) :data(p), nCount(1) { 
   } ~Assistant() { 
    delete data; } T* data; int nCount; }; template <typename T> class Smarter { 
    public: Smarter() { 
   }; Smarter(T* p) :pAssistant(new Assistant<T>(p)) { 
   } Smarter(const Smarter<T> &p) :pAssistant(p.pAssistant)//拷贝构造 { 
    ++pAssistant->nCount; } Smarter& operator=(const Smarter<T> &p)//赋值构造 { 
    ++p.pAssistant->nCount; if (pAssistant && --pAssistant->nCount == 0) delete pAssistant; pAssistant = p.pAssistant; return *this; } T& operator* () { 
    return *(pAssistant->data); } T* operator->() { 
    return pAssistant->data; } ~Smarter() { 
    std::cout << "nCount:" << pAssistant->nCount << std::endl; if (--pAssistant->nCount == 0) { 
    std::cout << "即将销毁" << std::endl; delete pAssistant; } } int count() { 
    if (pAssistant) return pAssistant->nCount; return 0; } private: Assistant<T> *pAssistant; }; int main() { 
    int *a = new int(1); { 
    Smarter<int> b = a; { 
    Smarter<int> c = b; { 
    Smarter<int> d; d = c; cout << "a:" << *a << endl; } cout << "a:" << *a << endl; } cout << "a:" << *a << endl; } cout << "a:" << *a << endl; getchar(); return 0; } 

四std::weak_ptr

weak_ptr 被设计为与 shared_ptr 共同工作,可以从一个 shared_ptr 或者另一个 weak_ptr 对象构造而来。weak_ptr 是为了配合 shared_ptr 而引入的一种智能指针,它更像是 shared_ptr 的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载 operator* 和 operator-> ,因此取名为 weak,表明其是功能较弱的智能指针。它的最大作用在于协助 shared_ptr 工作,可获得资源的观测权,像旁观者那样观测资源的使用情况。观察者意味着 weak_ptr 只对 shared_ptr 进行引用,而不改变其引用计数,当被观察的 shared_ptr 失效后,相应的 weak_ptr 也相应失效。

下面是一些成员函数及其简介

  1. weak_ptr w; //创建空 weak_ptr,可以指向类型为 T 的对象
  2. weak_ptr w(sp); //与 shared_ptr 指向相同的对象,shared_ptr 引用计数不变。T必须能转换为 sp 指向的类型
  3. w=p; //p 可以是 shared_ptr 或 weak_ptr,赋值后 w 与 p 共享对象
  4. w.reset(); //将 w 置空
  5. w.use_count(); //返回与 w 共享对象的 shared_ptr 的数量
  6. w.expired(); //若 w.use_count() 为 0,返回 true,否则返回 false
  7. w.lock(); //如果 expired() 为 true,返回一个空 shared_ptr,否则返回非空 shared_ptr
int main() { 
    std::shared_ptr<int> a; a.reset(new int(1)); std::weak_ptr<int> b = a; cout << "a:Count:" << a.use_count() << endl; cout << "b:Count:" << b.use_count() << endl; std::shared_ptr<int> c; if (!b.expired())//检查是否过期 { 
    c = b.lock(); *c = 2; } cout << "a=" << *a << endl; cout << "c=" << *c << endl; cout << "a:Count:" << a.use_count() << endl; cout << "b:Count:" << b.use_count() << endl; cout << "c:Count:" << c.use_count() << endl; cout << endl; std::weak_ptr<int> d = b; cout << "a:Count:" << a.use_count() << endl; cout << "b:Count:" << b.use_count() << endl; cout << "c:Count:" << c.use_count() << endl; cout << "d:Count:" << d.use_count() << endl; cout << endl; b.reset(); cout << "a:Count:" << a.use_count() << endl; cout << "b:Count:" << b.use_count() << endl; cout << "c:Count:" << c.use_count() << endl; cout << "d:Count:" << d.use_count() << endl; getchar(); return 0; } 
讯享网class Woman; class Man { 
    private: std::weak_ptr<Woman> m_wife; //std::shared_ptr<Woman> m_wife;  public: void setWife(std::shared_ptr<Woman> woman) { 
    m_wife = woman; } ~Man() { 
    std::cout << "kill man\n"; } }; class Woman { 
    private: std::weak_ptr<Man> m_husband; //std::shared_ptr<Man> m_husband;  public: void setHusband(std::shared_ptr<Man> man) { 
    m_husband = man; } ~Woman() { 
    std::cout << "kill woman\n"; } }; int main(int argc, char** argv) { 
    { 
    std::shared_ptr<Man> m(new Man()); std::shared_ptr<Woman> w(new Woman()); if (m && w) { 
    m->setWife(w); w->setHusband(m); } } getchar(); return 0; } 

如果将Man和Woman对象内的私有成员换成shared_ptr;那么他们就互相引用了,当作用域结束后,他们的引用技术仍然不为0,造成内存泄露。

五 boost库scope_ptr

因为一个作用域指针只是简单保存和独占一个内存地址,所以 boost::scoped_ptr 的实现就要比 std::auto_ptr 简单。 在不需要所有权传递的时候应该优先使用 boost::scoped_ptr 。 在这些情况下,比起 std::auto_ptr 它是一个更好的选择,因为可以避免不经意间的所有权传递。

int main() { 
    boost::scoped_ptr<int> a(new int); *a = 1; *a.get() = 2; a.reset(new int); //boost::scoped_ptr<int> b = a;//不允许 retur 0} 

六 、boost库scoped_array

作用域数组,同scope_ptr,也是独享所有权的。

七 如何选择智能指针

(1)如果程序要使用多个指向同一个对象的指针,应选择 shared_ptr。这样的情况包括:

  1. 将指针作为参数或者函数的返回值进行传递的话,应该使用 shared_ptr;
  2. 两个对象都包含指向第三个对象的指针,此时应该使用 shared_ptr 来管理第三个对象;
  3. STL 容器包含指针。很多 STL 算法都支持复制和赋值操作,这些操作可用于 shared_ptr,但不能用于unique_ptr(编译器发出 warning)和 auto_ptr(行为不确定)。

(4)在局部作用域(例如函数内部或类内部),且不需要将指针作为参数或返回值进行传递的情况下,如果对性能要求严格,使用 scoped_ptr 的开销较 shared_ptr 会小一些。

小讯
上一篇 2025-02-16 21:41
下一篇 2025-03-24 09:23

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/127511.html