> 文章列表 > c++ 智能指针

c++ 智能指针

c++ 智能指针

c++ 有三种智能指针

  • shared_ptr
  • weak_ptr
  • unique_ptr

头文件 <memory>

关于 auto_ptr 指针

C++98 提供了 auto_ptr 模板的解决方案, 在 c++11 被弃用,c++17中彻底移除,用 unique_ptr 代替

auto_ptr 被弃用的主要原因:
 1.复制或者赋值都会改变资源的所有权,在STL容器中有重大风险

auto_ptr<string> p1(new string("I'm P1!"));
auto_ptr<string> p2(new string("I'm P2."));
vector<auto_ptr<string>> vec;
// 必须使用move修饰成右值,才可以进行插入容器中
vec.push_back(move(p1));
vec.push_back(move(p2));cout << "vec[0]:" <<  *vec.[0] << endl; // 输出:I'm p1
cout << "vec[1]:" <<  *vec[1] << endl; // 输出:I'm p2// p2赋值给p1后,首先p1会先将自己原先托管的指针释放掉,然后接收托管p2所托管的指针,
// 然后p2所托管的指针指向NULL,也就是p1托管了p2托管的指针,而p2放弃了托管。
p1 = p2;// 风险来了:
vec[0] = vec[1];
cout << "vec[0]:" << *vec[0] << endl; // 输出: I'm p2
cout << "vec[1]:" << *vec[1] << endl; // ERROR, vec[1] 此时指向 nullptr

 2.不支持对象数组的内存管理

auto_ptr<int[]> array(new int[5]); // ERROR, 不能这样定义

unique_ptr

C++11用更严谨的 unique_ptr 取代了auto_ptr。

unique_ptr 用法:
 uniquer_ptr 是独占指针,只能有一个 unique_ptr 指针指向某个对象
 通过指针占有并管理另一对象,并在 unique_ptr 离开作用域时释放该对象。
 unique_ptr 可以不占有对象,此时 unique_ptr 为空

unique_ptr 有两个版本:

  1. 管理单个对象(例如以 new 分配)
  2. 管理动态分配的对象数组(例如以 new[] 分配)

类可以移动构造 (MoveConstructible) 和 移动赋值 (MoveAssignable);不能复制构造 (CopyConstructible) 和 复制赋值 (CopyAssignable) 。(只能有一个 unique_ptr 指针指向某个对象)

构造方式

#include <iostream>
#include <memory>
using namespace std;struct Foo { // object to manageFoo() { cout << "Foo ctor" << endl; }Foo(const Foo&) { cout << "Foo copy ctor" << endl; }Foo(Foo&&) { cout << "Foo move ctor" << endl; }~Foo() { cout << "~Foo dtor" << endl; }
};struct D { // deleterD() {};D(const D&) { cout << "D copy ctor" << endl; }D(D&) { cout << "D non-const copy ctor" << endl;}D(D&&) { cout << "D move ctor " << endl; }void operator()(Foo* p) const {cout << "D is deleting a Foo" << endl;delete p;};
};int main()
{cout << "Example constructor(1)..." << endl;unique_ptr<Foo> up1;  // up1 is emptyunique_ptr<Foo> up1b(nullptr);  // up1b is emptycout << "Example constructor(2)..." << endl;{unique_ptr<Foo> up2(new Foo); //up2 now owns a Foo} // Foo deletedcout << "Example constructor(3)..." << endl;D d;{  // deleter type is not a referenceunique_ptr<Foo, D> up3(new Foo, d); // deleter copied}{  // deleter type is a reference unique_ptr<Foo, D&> up3b(new Foo, d); // up3b holds a reference to d}cout << "Example constructor(4)..." << endl;{  // deleter is not a reference unique_ptr<Foo, D> up4(new Foo, D()); // deleter moved}cout << "Example constructor(5)..." << endl;{unique_ptr<Foo> up5a(new Foo);unique_ptr<Foo> up5b(move(up5a)); // ownership transfer}cout << "Example constructor(6)..." << endl;{unique_ptr<Foo, D> up6a(new Foo, d); // D is copiedunique_ptr<Foo, D> up6b(move(up6a)); // D is movedunique_ptr<Foo, D&> up6c(new Foo, d); // D is a referenceunique_ptr<Foo, D> up6d(move(up6c)); // D is copied}cout << "Example array constructor..." << endl;{unique_ptr<Foo[]> up(new Foo[3]);} // three Foo objects deleted
}

注意:

 只有非 const 的 unique_ptr 能转移被管理对象的所有权给另一 unique_ptr 。若对象的生存期为 const unique_ptr 所管理,则它被限定在创建指针的作用域中。

shared_ptr

共享指针,是为多个所有者可能必须管理对象在内存中的生命周期的方案设计。 在初始化一个 shared_ptr 之后,可复制它,按值将其传入函数参数,然后将其分配给其他 shared_ptr 实例。 所有实例均指向同一个对象,并共享对一个“控制块”(每当新的 shared_ptr 添加、超出范围或重置时增加和减少引用计数)的访问权限。 当引用计数达到零时,控制块将删除内存资源和自身。

简单实现 shared_ptr

#include <iostream>
#include <mutex>
using namespace std;template <typename T>
class myShared_ptr{
public:myShared_ptr(T* ptr = nullptr): _ptrCount(new int(1)),_ptr(ptr),_mutex(new mutex()) {}myShared_ptr(myShared_ptr& sp): _ptrCount(sp._ptrCount),_ptr(sp._ptr),_mutex(sp._mutex) {}~myShared_ptr() {release();}myShared_ptr<T> & operator=(const myShared_ptr<T>& sp) {if (_ptr != sp._ptr) {release();_ptr = sp._ptr;_ptrCount = sp._ptrCount;_mutex = sp._mutex;addCount();}return *this;}T&  operator*() { return *_ptr; }T*  operator->() { return _ptr; }int useCount() { return *_ptrCount; }T*  get() { return _ptr; }private:void release() {_mutex->lock();bool flagMutex = false;if (--(*_ptrCount) == 0) {delete _ptrCount;delete _ptr;flagMutex = true;}_mutex->unlock();if (flagMutex)delete _mutex;}void addCount() {_mutex->lock();++(*_ptrCount);_mutex->unlock();}private:int* _ptrCount;T* _ptr;mutex* _mutex;
};class B;
class A {
public:A() { cout << "constuctor A" << endl; }~A() { cout << "deconstuctor A" << endl; }
};class B {
public:B() { cout << "constuctor B" << endl; }~B() { cout << "deconstuctor B" << endl; }
};int main()
{A *a = new A();B *b = new B();myShared_ptr<A> aPtr(a);myShared_ptr<B> bPtr(b);cout << aPtr.useCount() << endl;cout << bPtr.useCount() << endl;return 0;
}// 输出
constuctor A
constuctor B
1
1
deconstuctor B
deconstuctor A

weak_ptr

为了解决 shared_ptr 循环引用的问题而引入。
Weak_ptr允许共享,但不拥有一个对象。 它的对象是由shared_ptr创建的

class B;
class A {
public:shared_ptr<B> _b;A() { cout << "constuctor A" << endl; }~A() { cout << "deconstuctor A" << endl; }
};class B {
public:shared_ptr<A> _a;B() { cout << "constuctor B" << endl; }~B() { cout << "deconstuctor B" << endl; }
};int main()
{shared_ptr<A> aPtr = make_shared<A>();shared_ptr<B> bPtr = make_shared<B>();cout << aPtr.use_count() << endl;  // 输出 1cout << bPtr.use_count() << endl;  // 输出 1return 0;
}//输出
constuctor A
constuctor B
1
1
deconstuctor B
deconstuctor A

此时 A 和 B 正常析构。

int main()
{shared_ptr<A> aPtr = make_shared<A>();shared_ptr<B> bPtr = make_shared<B>();cout << aPtr.use_count() << endl;  // 输出 1cout << bPtr.use_count() << endl;  // 输出 1aPtr->_b = bPtr;bPtr->_a = aPtr;cout << aPtr.use_count() << endl; // 输出 2cout << bPtr.use_count() << endl; // 输出 2return 0;
}
// 输出
constuctor A
constuctor B
1
1
2
2

没有调用 A 和 B 的析构函数。aPtr 和 bPtr 的引用计数都为 2。
此时就会造成内存泄漏,所以需要 weak_ptr 指针

class B;
class A {
public:weak_ptr<B> _b; // 这里把 shared_ptr 换成 weak_ptrA() { cout << "constuctor A" << endl; }~A() { cout << "deconstuctor A" << endl; }
};class B {
public:weak_ptr<A> _a;  // 这里把 shared_ptr 换成 weak_ptrB() { cout << "constuctor B" << endl; }~B() { cout << "deconstuctor B" << endl; }
};int main()
{shared_ptr<A> aPtr = make_shared<A>();shared_ptr<B> bPtr = make_shared<B>();cout << aPtr.use_count() << endl;  // 输出 1cout << bPtr.use_count() << endl;  // 输出 1aPtr->_b = bPtr;bPtr->_a = aPtr;cout << aPtr.use_count() << endl;cout << bPtr.use_count() << endl; return 0;
}// 输出
constuctor A
constuctor B
1
1
1
1
deconstuctor B
deconstuctor A

脑瘫疑难解答