赞
踩
C++ 程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。但使用普通指针,容易造成内存泄露(忘记释放)、二次释放、程序发生异常时内存泄露等问题等。所以 C++11 就引入了智能指针。
C 语言中最常使用的是malloc()函数分配内存,free()函数释放内存,而 C++ 中对应的是new、delete关键字。malloc()只是分配了内存,而new则更进一步,不仅分配了内存,还调用了构造函数进行初始化。
C++11 中引入了智能指针(Smart Pointer):它利用了一种叫做 RAII(资源获取即初始化)的技术将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。这使得智能指针实质是一个对象,行为表现的却像一个指针。
智能指针主要分为shared_ptr、unique_ptr和weak_ptr三种,使用时需要引用头文件。C++98 中还有auto_ptr,基本被淘汰了,不推荐使用。而 C++11 中shared_ptr和weak_ptr都是参考boost库实现的
shared_ptr是使用最多的一种。多个智能指针可以指向同一个对象,即实现了资源的多指针共享。shared_ptr引入了计数机制,可以通过use_count成员函数查看资源所有者的个数。当一个shared_ptr指向该对象,则计数加一,当一个shared_ptr释放,计数减一。当最后一个指针释放,就释放内存。
代码如下(示例):
include<mutex> template<typename T> class Shared_ptr { private: // 成员变量 T *ptr_{nullptr}; int *ref_count_{nullptr}; std::mutex *mutex_{nullptr}; public: /******************** 构造函数和析构函数 ********************/ // 默认构造函数 constexpr Shared_ptr() noexcept = default; // 普通构造函数 explicit Shared_ptr(T *ptr) : ptr_{ptr} { if (this->ptr_ != nullptr) { this->ref_count_ = new int{1}; this->mutex_ = new std::mutex{}; } } // 拷贝构造函数 Shared_ptr(const Shared_ptr &rhs) noexcept: ptr_{rhs.ptr_}, ref_count_{rhs.ref_count_}, mutex_{rhs.mutex_} { if (this->ptr_ != nullptr) { this->addRefCount(); } } // 移动构造函数 Shared_ptr(Shared_ptr &&rhs) noexcept: ptr_{rhs.ptr_}, ref_count_{rhs.ref_count_}, mutex_{rhs.mutex_} { // right hand side is nullptr rhs.ptr_ = nullptr; rhs.ref_count_ = nullptr; rhs.mutex_ = nullptr; } // 析构函数 ~Shared_ptr() noexcept { this->decrRefCount(); } /******************** 运算符重载 ********************/ // 拷贝赋值运算符 Shared_ptr &operator=(const Shared_ptr &rhs) { Shared_ptr{rhs}.swap(*this); return *this; } // - move 本意为 "移动",但该函数并不能移动任何数据,它的功能很简单,就是将某个左值强制转化为右值。 // - forward 完美转发,根据右值判断的推倒,调用forward 传出的值,若原来是一个右值,那么他转出来就是一个右值,否则为一个左值。这样的处理就完美的转发了原有参数的左右值属性,不会造成一些不必要的拷贝 // 移动赋值运算符 Shared_ptr &operator=(Shared_ptr &&rhs) noexcept { Shared_ptr{std::move(rhs)}.swap(*this); return *this; } // 取值 T &operator*() const noexcept { return *this->ptr_; } // 取指针 T &operator->() const noexcept { return this->ptr_; } // 到 bool 的隐式转换 explicit operator bool() const noexcept { return static_cast<bool>(ptr_); } /******************** 成员函数 ********************/ // 获取指针 T *get() const noexcept { return ptr_; } // 获取引用计数 int use_count() const noexcept { return this->ref_count_ == nullptr ? 0 : *this->ref_count_; } // 引用计数是否唯一 bool unique() const noexcept { return *this->ref_count_ == 1; } // 重置指针 void reset() noexcept { Shared_ptr{}.swap(*this); } // 重置指针 void reset(T *ptr) { Shared_ptr{ptr}.swap(*this); } // swap函数 void swap(Shared_ptr &rhs) noexcept { std::swap(this->ptr_, rhs.ptr_); std::swap(this->ref_count_, rhs.ref_count_); std::swap(this->mutex_, rhs.mutex_); } private: // 增加引用计数 void addRefCount() { mutex_->lock(); ++(*ref_count_); mutex_->unlock(); } // 减少引用计数,如果为零,则释放指针 void decrRefCount() { bool deleteflag = false; mutex_->lock(); if (--(*ref_count_) == 0) { delete ptr_; delete ref_count_; deleteflag = true; } mutex_->unlock(); if (deleteflag) { delete mutex_; } } }; // 函数模板 template<typename T> auto make_Shared(T v) { return Shared_ptr<T>{new T(v)}; }
代码如下(示例):
// 初始化 shared_ptr<int> p1 = make_shared<int>(42); shared_ptr<int> p2(new int(1024)); //上面用构造函数创建,等价于 //int* ptr = new int(1024); // std::shared_ptr<int> p1(ptr); // std::shared_ptr<int> p2(ptr);//错误 不能同时赋值给多个智能指针 shared_ptr<int> p3 = p2; // 获取值 cout << p1.get() << endl; cout << *p1.get() << endl; cout << *p1 << endl; // 引用计数 cout << p1.use_count() << endl; cout << p2.use_count() << endl; cout << p3.use_count() << endl; // 引用计数是否唯一 cout << p1.unique() << endl; // 重置 p1.reset(new int(322)); // swap p1.swap(p2);
在大量数据转移时候,有时候可能转几手,自己都忘记如何释放,或者在不同线程中,之前的做法为了小心翼翼的避免内存泄露,都要一一对应,现在只需要用智能指针去处理,不用担心释放问题,(实际过程中需要避免循环引用)
代码如下(示例):
//暂无,没有想好怎么表达
弱指针主要是为了避免shared_ptr循环引用问题:当两个对象相互使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
当出现循环引用时,把其中一个shared_ptr转化成weak_ptr即可解决问题。
shared_ptr<int> p = make_shared<int>(100);
weak_ptr<int> w(p); // 与p指向相同对象的weak_ptr,
w = p; // p可以是shared_ptr或者weak_ptr,赋值后w和p共享对象
w.reset(); // weak_ptr置为空
w.use_count(); // 与w共享对象的shared_ptr的计数
w.expired(); // w.use_count()为0则返回true,否则返回false
w.lock(); // w.expired()为true,返回空的shared_ptr;否则返回指向w的shared_ptr
unique_ptr相对于其他两个智能指针更加简单,它和shared_ptr使用差不多,但是功能更为单一,它是一个独占型的智能指针,不允许其他的智能指针共享其内部的指针,更像原生的指针(但更为安全,能够自己释放内存)。不允许赋值和拷贝操作,只能够移动。
使用智能指针虽然能够解决内存泄漏问题,但是也付出了一定的代价。以shared_ptr举例:
1、c++11智能指针解析——揭开底层面纱,完整理解智能指针
2、C++11 智能指针【详解+实现】【面试常考】
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。