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));

// move 后 p1 p2 都指向空指针
if (p1.get() == nullptr)
    cout << "p1 is nullptr" << endl;

if (p2.get() == nullptr)
    cout << "p2 is nullptr" << endl;

cout << "vec[0]: " <<  *vec[0] << endl; // 输出:I'm p1
cout << "vec[1]: " <<  *vec[1] << endl; // 输出:I'm p2

// 风险来了:
// vec[1] 赋值给 vec[0] 后,首先 vec[1] 会先将自己原先托管的指针释放掉,然后接收托管 vec[2] 所托管的指针,
// 然后 vec[2] 所托管的指针指向NULL,也就是 vec[1]托管了 vec[2] 托管的指针,而 vec[2] 放弃了托管。
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 A { // object to manage
    A() { cout << "A ctor" << endl; }
    A(const A&) { cout << "A copy ctor" << endl; }
    A(A&&) { cout << "A move ctor" << endl; }
    ~A() { cout << "~A dtor" << endl; cout << endl; }
};

struct D { // deleter
    D() {}
    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()(A* p) const {
        cout << "D is deleting a A" << endl;
        delete p;
    }
};

int main()
{
    unique_ptr<A> up1;  // up1 is empty
    unique_ptr<A> up1b(nullptr);  // up1b is empty

    {
        printf("*******up2******\n");
        unique_ptr<A> up2(new A); //up2 now owns a A
    } // A deleted


    D d;

    {
        printf("*******up3, up3b******\n");
        // deleter type is not a reference
        unique_ptr<A, D> up3(new A, d); // deleter copied

        // deleter type is a reference
        unique_ptr<A, D&> up3b(new A, d); // up3b holds a reference to d
    }

    {
        printf("*******up4******\n");
        // deleter is not a reference
        unique_ptr<A, D> up4(new A, D()); // deleter moved
    }

    {
        printf("*******up5******\n");
        unique_ptr<A> up5a(new A);
        unique_ptr<A> up5b(move(up5a)); // ownership transfer
    }

    {
        printf("*******up6******\n");
        unique_ptr<A, D> up6a(new A, d); // D is copied
        unique_ptr<A, D> up6b(move(up6a)); // D is moved

        unique_ptr<A, D&> up6c(new A, d); // D is a reference
        unique_ptr<A, D> up6d(move(up6c)); // D is copied
    }

    {
        printf("*******up array******\n");
        unique_ptr<A[]> up(new A[3]);
    } // three A 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(const myShared_ptr& sp)
        : _ptrCount(sp._ptrCount), _ptr(sp._ptr), _mutex(sp._mutex) {
        addCount();
    }

    ~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*() const { return *_ptr; }
    T*  operator->() const { return _ptr; }
    int useCount() const { return *_ptrCount; }
    T*  get() const { return _ptr; }

private:
    void release() {
        _mutex->lock();
        bool flagMutex = false;
        if (--(*_ptrCount) == 0) {
            delete _ptrCount;
            if (_ptr)
                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 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()
{
    shared_ptr<A> aPtr = make_shared<A>();
    shared_ptr<B> bPtr = make_shared<B>();

    cout << aPtr.use_count() << endl;  // 输出 1
    cout << bPtr.use_count() << endl;  // 输出 1
    return 0;
}

//输出 此时 A 和 B 正常析构。
constuctor A
constuctor B
1
1
deconstuctor B
deconstuctor A

改成如下代码:

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;  // 输出 1
    cout << bPtr.use_count() << endl;  // 输出 1

    aPtr->_b = bPtr; // 令_b 指向 bPtr
    bPtr->_a = aPtr; // 令_a 指向 aPtr

    cout << aPtr.use_count() << endl; // 此时引用变为 2
    cout << bPtr.use_count() << endl; // 此时引用变为 2

    return 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_ptr
    A() { cout << "constuctor A" << endl; }
    ~A() { cout << "deconstuctor A" << endl; }
};

class B {
public:
    weak_ptr<A> _a;  // 这里把 shared_ptr 换成 weak_ptr
    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;  // 输出 1
    cout << bPtr.use_count() << endl;  // 输出 1

    aPtr->_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
posted @ 2024-12-07 13:17  卑以自牧lq  阅读(4)  评论(0编辑  收藏  举报