C++智能指针学习

C++智能指针学习,第1张

不使用智能指针

在不使用智能指针的情况下,如果 new 出来了一个指针,那么必须在程序里手动 delete 一下。


但是如果在 delete 之前程序就 return 了,那么就会导致资源泄露。


直到程序退出, *** 作系统才会释放这段内存。


但是如果程序一直没有退出,这段内存就会一直没有释放。


危害,如下:

#include 
using namespace std;
int main() {
    int* ptr = new int(0);
    if (...) {
        return -1;
    }
    delete ptr;
    return 0;
}

代码量少,资源少可能体现不太出来。


但是 new 出来的东西太多了都没有及时释放就会很糟糕了。


手写智能指针

把指针放到对象里,调用析构函数的时候释放指针指向的内存。


如下面代码所示:

#include 
using namespace std;

template
class MySmartPtr {
public:
    MySmartPtr(T* ptr):_ptr(ptr){}
    ~MySmartPtr() {
        delete _ptr;
    }
    T& operator*() {
        return *_ptr;
    }
private:
    T* _ptr;
};

int main() {
    MySmartPtr ptr(new int);
    if (...) {
        return -1;
    }
    return 0;
 }

在 main 函数里定义了 ptr 对象,把 new 出来的内存传给这个对象。


ptr 是在栈里的,当 ptr 离开了 main 函数的作用域,由于栈里的东西会被释放,即 ptr 会调用西沟函数,就会释放堆里的刚刚 new 出来的内存。


非引用计数型智能指针

首先看一下下面的代码:

#include 
using namespace std;

template
class MySmartPtr {
public:
    MySmartPtr(T* ptr):_ptr(ptr){}
    ~MySmartPtr() {
        delete _ptr;
    }
    T& operator*() {
        return *_ptr;
    }
private:
    T* _ptr;
};

int main() {
    MySmartPtr ptr(new int);
    MySmartPtr ptr2(ptr);
    return 0;
 }

运行会报错,ptr2 析构后,ptr 和 ptr2 指向的内存已经被释放了。


就成了野指针,再在 ptr 析构的时候在释放一次就报错。


auto_ptr

下面的代码可以正常输出。


#include 
#include 
using namespace std;

int main() {
    auto_ptr ptr(new int(0));
    cout << *ptr << endl;
    return 0;
 }

 

 下面的代码报错:

#include 
#include 
using namespace std;

int main() {
    auto_ptr ptr(new int);
    auto_ptr ptr2(ptr);
    cout << *ptr << endl;
    return 0;
 }

注意,是 cout << *ptr << endl; 这行报错,不是 auto_ptr ptr2(ptr);


下面是 auto_ptr 的拷贝构造函数:

auto_ptr(auto_ptr& _Right) noexcept : _Myptr(_Right.release()) {}

/*
    _Ty* release() noexcept {
        _Ty* _Tmp = _Myptr;
        _Myptr    = nullptr;
        return _Tmp;
    }
*/

也就是说被拷贝的指针指向 nullptr 了,因此输出指针指向的值会报错。


这就是非计数的意思吧。


unique_ptr
#include 
#include 
using namespace std;

int main() {
    unique_ptr ptr(new int(0));
    unique_ptr ptr2(ptr);
    cout << *ptr << endl;
    return 0;
 }

上面的代码也会报错,和 auto_ptr 不同的是,在 unique_ptr ptr2(ptr); 这行出现错误。


看看  unique_ptr 的拷贝构造和赋值构造:

unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;

直接不允许。


但是 uniqu_ptr 可以进行右值类型的拷贝构造:

#include 
#include 
using namespace std;

int main() {
    unique_ptr ptr(new int(0));
    unique_ptr ptr2(move(ptr));
    cout << *ptr << endl;
    return 0;
 }

上面的代码会在 cout << *ptr << endl; 这行报错。


引用计数型智能指针

当指针指向内存的时候,引用计数++,否则--。


当引用计数 == 0 的时候,内存才算释放掉了。


shared_ptr
#include 
#include 
using namespace std;

int main() {
    shared_ptr ptr(new int(0));
    shared_ptr ptr2(ptr);
    cout << ptr.use_count() << endl;
    return 0;
 }

输出 2.

shared_ptr 在多个对象的成员互指的时候可能出现内存泄露。


 这样把 A 和 B 放到智能指针里的话引用计数都是 2 了。


结果就是怎么都不会减到 0.

#include 
#include 
using namespace std;

class B;
class A {
public:
    shared_ptr b;
};

class B {
public:
    shared_ptr a;
};

int main() {
    shared_ptr ptrA(new A());
    shared_ptr ptrB(new B());
    ptrA->b = ptrB;
    ptrB->a = ptrA;
    cout << ptrA.use_count() << endl;
    cout << ptrB.use_count() << endl;
    return 0;
 }

输出:

2
2

解决办法,就是使用下面的 weak_ptr.

weak_ptr

只在定义的时候使用 shared_ptr.

#include 
#include 
using namespace std;

class B;
class A {
public:
    weak_ptr b;
};

class B {
public:
    weak_ptr a;
};

int main() {
    shared_ptr ptrA(new A());
    shared_ptr ptrB(new B());
    ptrA->b = ptrB;
    ptrB->a = ptrA;
    cout << ptrA.use_count() << endl;
    cout << ptrB.use_count() << endl;
    return 0;
 }

输出:

1
1

总结,shared_ptr 一下,引用计数++,weak_ptr 一下,引用计数不++。


那有一个问题,shared_ptr 类型的对象引用计数为 0 了,但是被 weak_ptr 了一下。


weak_ptr 之后怎么发现这个这问题呢?使用 lock() 方法强转一下,再判断一下强转之后指针是不是为空是不是还能用。


shared_ptr 就像名字一样,很弱,要用的话得强转成shared_ptr.

案例1:

#include 
#include 
using namespace std;

class A {
public:
    void f() {
        cout << "A.f()" << endl;
    }
};

int main() {
    shared_ptr ptrA(new A());
    weak_ptr ptrB(ptrA);
    cout << ptrA.use_count() << endl;
    cout << ptrB.use_count() << endl;
    shared_ptr ptrC = ptrB.lock();
    ptrC->f();
    cout << ptrA.use_count() << endl;
    cout << ptrB.use_count() << endl;
    cout << ptrC.use_count() << endl;
    return 0;
 }

输出:

1
1
A.f()
2
2
2

 案例2:

#include 
#include 
using namespace std;

class B;
class A {
public:
    weak_ptr b;
    void fA() {
        cout << "fA " << b.use_count() << endl;
    }
};

class B {
public:
    weak_ptr a;
    void fB() {
        shared_ptr pa = a.lock();
        if (pa) {
            pa->fA();
            cout << "fB " << pa.use_count() << endl;
        }
    }
};

int main() {
    shared_ptr ptrA(new A());
    shared_ptr ptrB(new B());
    ptrA->b = ptrB;
    ptrB->a = ptrA;
    cout << ptrA.use_count() << endl;
    cout << ptrB.use_count() << endl;
    ptrB->fB();
    return 0;
 }

输出:

1
1
fA 1
fB 2

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/langs/564973.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-06
下一篇 2022-04-06

发表评论

登录后才能评论

评论列表(0条)

保存