五、C++语言进阶:智能指针

五、C++语言进阶:智能指针,第1张

5 智能指针
  • 指针的危害
    指针未初始化
    野指针
    内存泄漏

智能指针的出现就是为了解决上面的问题。
智能指针基于这样的事实得以发挥作用:定义在栈中的智能指针,当超出其作用域时,会自动调用它的析构函数,从而可以释放其关联的内存资源。

5.1 分类
智能指针进入C++标准的版本STL头文件boost头文件说明
auto_ptrC++03尽量避免使用
unique_ptrC++11管理单个堆内存对象,独享所有权,不允许赋值和拷贝,不能用于管理数组对象
shared_ptrC++11引用计数智能指针,共享所有权
weak_ptrC++11shared_ptr的观察者,只进行引用而不改变引用计数,用来解决shared_ptr的循环引用问题
scoped_ptr--作用域智能指针,功能与unique_ptr相似。
  • 本质:
    将指针封装为类对象成员,并在析构函数里删除指针指向的内存。
  • 不同:
    auto_ptrunique_ptrscoped_ptr马上删除。
    shared_ptr计数为0删除。
    weak_ptr不删除。
5.2 auto_ptr
  • 作用
    对作用域内的动态分配对象的自动释放
  • 基本类型
#include 
#include 
using namespace std;
int main(){
    int* pn = new int(10);
    auto_ptr<int> ap(pn);
    cout << *ap << endl;
}
  • 类类型
#include 
#include 
using namespace std;
class Test{
public:
    Test(){cout << __func__ << endl;}
    ~Test(){cout << __func__ << endl;}
    void Func(){cout << __func__ << endl;}
};
int main(){
    Test* pt = new Test;
    auto_ptr<Test> apt(pt);
    apt->Func();
}
  • 缺陷

(1)两个auto_ptr不能同时拥有同一个对象

#include 
using namespace std;
int main(){
    int* pn = new int(10);
    auto_ptr<int> ap1(pn);
    auto_ptr<int> ap2(pn);
}

(2)auto_ptr不能管理数组指针

#include 
using namespace std;
int main(){
    int *pa=new int[10];
    auto_ptr<int>ap(pa);
}

(3)auto_ptr被拷贝或被赋值后,失去对原指针的管理,这种情况被称为指针所有权传递。

a)赋值的情况

  • 实例
#include 
#include 
using namespace std;
int main(){
    int* p = new int(10);
    auto_ptr<int> ap1(p);
    cout<< *ap1 <<endl;
    auto_ptr<int> ap2=ap1;
    cout<< *ap1 <<endl;
}

b)拷贝的情况

  • 实例
#include 
#include 
using namespace std;
void Func(auto_ptr<int> ap){
    cout << *ap << endl;
}
int main(){
    int* p = new int(10);
    auto_ptr<int> ap(p);
    cout<< *ap <<endl;
    Func(ap);
    cout<< *ap <<endl;
}
auto_ptr不能作为容器对象,因为STL容器中的元素经常要支持拷贝,赋值等 *** 作。
#include 
#include 
#include 
using namespace std;
int main(){
    int*p = new int(10);
    auto_ptr<int> ap(p);
    vector<auto_ptr<int> > vec;
    vec.push_back(ap);
}


(4)auto_ptr不能作为容器对象,因为STL容器中的元素经常要支持拷贝,赋值等 *** 作。

#include 
#include 
#include 
using namespace std;
int main(){
    int*p = new int(10);
    auto_ptr<int> ap(p);
    vector<auto_ptr<int> > vec;
    vec.push_back(ap);
}
  • 实例
#include 
#include 

using namespace std;

class Test{
public:
    Test(){cout << __func__ << endl;}
    ~Test(){cout << __func__ << endl;}
    void func(int n)const{
    	cout << n << endl;
    }
};

namespace MySTL{
template <typename T>
class auto_ptr{
    T* p;
public:
    auto_ptr(T* p):p(p){}
    ~auto_ptr(){
    	delete p;
    }
    T* operator->(){
    	return p;
    }
    T& operator*(){
    	return *p;
    }
};
};

int main(){
    Test* pp = new Test;
    MySTL::auto_ptr<Test> p(new Test); //p智能指针
    p->func(10);
    p.operator->()->func(100); //p.operator->()返回的是p指针

    (*p).func(100);
    delete pp;

    int* arr = new int [10];
    delete [] arr;
}

结果:

Test
Test
10
100
100
~Test
~Test

分析:将指针封装为类对象成员,并在析构函数里删除指针指向的内存。

5.3 unique_ptr
  • 作用
    代替auto_ptr,不可复制与赋值。
5.4 scoped_ptr
  • 作用
    unique_ptr相似,在本作用域中使用,不能复制与赋值。
5.5 shared_ptr
  • 作用
    解决unique_ptr禁用拷贝以及auto_ptr的缺陷

  • 实例

#include 
#include 

using namespace std;

class Test {
public:
    Test() {
        cout << __func__ << endl;
    }
    ~Test() {
        cout << __func__ << endl;
    }
    void func(int n)const {
        cout << n << endl;
    }
};

namespace MySTL {
template <typename T>
class auto_ptr {
    T* p;
public:
    auto_ptr(T* p):p(p) {}
    ~auto_ptr() {
        delete p;
    }
    T* operator->() {
        return p;
    }
    T& operator*() {
        return *p;
    }
};

template<typename T>
class shared_ptr {
    struct Record {
        T* p;
        int count;
    };
    Record* record;
public:
    shared_ptr(T* p) {
        record = new Record{p,1};
    }
    shared_ptr(const shared_ptr& ptr) {
        record = ptr.record;
        ++record->count;
    }
    T* operator->() {
        return record->p;
    }
    T& operator*() {
        return *(record->p);
    }

    shared_ptr& operator=(const shared_ptr& ptr) {
        if(this == &ptr) return *this;
        if(record->p == ptr.record->p) return *this;
        --record->count;
        if(record->count == 0) {
            delete record->p;
            delete record;
        }
        record = ptr.record;
        ++record->count;
        return *this;
    }
    ~shared_ptr() {
        --record->count;
        if(0 == record->count) {
            delete record->p;
            delete record;
        }
    }
};
};
int main() {
    // Test* pp = new Test;
    MySTL::shared_ptr<Test> p(new Test); //p智能指针
    p->func(10);
    p.operator->()->func(100); //p.operator->()返回的是p指针

    (*p).func(100);
    // delete pp;

    // int* arr = new int [10];
    // delete [] arr;

    MySTL::shared_ptr<Test> p2 = p;
    p2->func(300);
    p->func(400);

    MySTL::shared_ptr<Test> p3(new Test);
    p3 = p;
    p3->func(500);
}

结果:

Test
10
100
100
300
400
Test
~Test
500
~Test

分析:
通过加入Record记录指向该块内存的指针数量,在析构时候避免重复释放内存。

  • 存在的问题:循环引用问题
  • 实例
#include 
#include 
using namespace std;
class A;
class B;
class A{
public:
    shared_ptr<B> pb;
    A (){
        cout << __func__ << endl;
    }
    ~A (){
        cout << __func__ << endl;
    }
};
class B{
public:
    shared_ptr<A> pa;
    B (){
        cout << __func__ << endl;
    }
    ~B (){
        cout << __func__ << endl;
    }
};
int main(){
    shared_ptr<A> a(new A);
    shared_ptr<B> b(new B);
    //A和B出现循环引用
    a->pb = b;
    b->pa = a;
}

结果:

A
B

分析:
只构造不析构,A和B出现循环引用

5.6 weak_ptr
  • 作用
    解决shared_ptr循环引用问题
  • 实例
#include 
#include 
using namespace std;
class A;
class B;
class A{
public:
    weak_ptr<B> pb;
    A (){
        cout << __func__ << endl;
    }
    ~A (){
        cout << __func__ << endl;
    }
};
class B{
public:
    weak_ptr<A> pa;
    B (){
        cout << __func__ << endl;
    }
    ~B (){
        cout << __func__ << endl;
    }
};
int main(){
    shared_ptr<A> a(new A);
    shared_ptr<B> b(new B);
    //A和B出现循环引用
    a->pb = b;
    b->pa = a;
}

结果:

A
B
~B
~A

智能指针weak_ptr主要用来协助shared_ptr。不参与引用计数,但是有以下好处:
1 打破递归的依赖关系
2 使用一个共享的资源但是不要所有权,不添加引用计数
3 避免悬空指针。

5.7 总结
  • 智能指针的特点
智能指针管理同一个对象可拷贝可复制所有权成为容器元素管理数组指针
auto_ptrNGOKOK传递NGNG
unique_ptrNGNGNG独享NGNG
scoped_ptrNGNGNG独享NGNG
shared_ptrNGOKOK共享OKNG
weak_ptrNGOKOK共享OKNG

虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案。

如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。
因此,不要认为只要使用了智能指针便能杜绝内存泄漏。

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

原文地址: http://outofmemory.cn/langs/1499020.html

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

发表评论

登录后才能评论

评论列表(0条)

保存