STL学习-智能指针-shared_ptr和weak_ptr
一.shared_ptr
shared ptr共享智能指针,多个shared ptr可以指向相同的对象,在内部有一个
关联的计数器。通常称其为引用计数。
引用计数是一种内存管理技术,用于跟踪共享资源的引用情况,并在引用为0时进行
释放。引用计数使用一个计数器来统计当前指针指向的对象被多少类的对象所使用,即
记录指针指向对象被引用的次数。当新的shared ptr与对象关联时,引用计数加1。当
shared ptr结束时,引用计数减1。当引用计数变为0时,表示没有任何shared ptr与
对象关联,则释放该对象。
1.类定义
以下是shared_ptr的部分源码
template<class Ty>
class shared ptr {
public:
typedef Ty element type;
//默认构造,同下
shared ptr();
shared ptr(nullptr t);//同上
shared ptr(const shared ptr& sp);//可以拷贝构造
template<class other>
explicit shared ptr(0ther *ptr);//不允许隐式(必须显式)转换
template<class ther,class D>
shared ptr(other*ptr,D dtor);//可以提供自定义删除操作
template<class Other>
explicit shared ptr(const weak ptr<other>& wp);//可以显式智
template<class Other,class D>
shared ptr(unique ptr<other,D>&& up);//可以指针转换
shared ptr& operator=(const shared ptr& sp);//可以赋值//成员函数
void swap(shared ptr& sp);//交换
void reset();//重置
template<class Other>
void reset(other *ptr);
template<class Other,class D>
void reset(Other *ptr,D dtor);
template<class Other,class D,class A>
void reset(Other *ptr,D dtor, A alloc)
Ty *get();//获取源(裸)指针
Ty& operator*() const;//支持 *
Ty *operator->() const;//支持 ->
long use count() const;//获得引用计数的数量
bool unique() const;//是否唯一Ty *get()const;
operator bool()const;
template<class Other>
bool owner before shared ptrOther> const& ptr) const
template<class Other>
bool owner before(weak ptr<Other>const& ptr)const;
template<class D,class Ty>
D* get deleter(shared ptr<Ty> const& ptr);
};
2.初始化
//方法一
shared_ptr<student>p1(new student("孙悟空"));// 分配内存并初始化。
//方法二
shared_ptr<student>p2 = make_unique<Student>("猪八戒");//C++14标准,推荐使用
//方法三:
Student* ps = new student("沙僧");
shared_ptr<student>p3(ps);//用已存在的地址初始化。
//方法四:
shared_ptr<student>p4(p3);//利用p3初始化p4
//方法五:
shared_ptr<student>p5=p3;//利用p3初始化p5
注意,上面p4,p5并没有创建新的"沙僧",它们只是增加了"沙僧"对象的引用计数,当引用计数为0是,对象被释放。
3.成员函数和运算符
//get具体用法
void test01(Student* ps)
{
ps->show();
}
int main()
{
shared ptr<student>p(new student("白骨精"));
test01(p.get());
return 0;
}//reset具体用法
int main()
{
shared ptr<student>p1(new student("孙悟空"));
shared_ptr<student>p2(p1);//p1,p2都指向"孙悟空”,引用计数为2
shared_ptr<student>p3(new student("天蓬元帅"));//引用计数为1
p1.reset(new student("齐天大圣"));
p3.reset(new student("猪八戒"));
p1->show();
p2->show();
p3->show();
return 0;
}
/*说明:
1.第6行,p1指向"齐天大圣”,"孙悟空"的引用计数由2变为1,不删除。
2.第7行,p3指向"猪八戒”,"天蓬元帅”的引用计数由1变为0,删除。*///use_count具体用法
int main()
{
shared ptr<Student>p1(new student("东海龙王"));
shared ptr<student>p2 = p1;
shared ptr<student>p3;
p3 = p1;
//p1,p2,p3都指向"东海龙王"
cout<<"p1的源指针:"<< p1.get()<< endl;
cout<<"p2的源指针:"<< p2.get()<<endl;
cout<<"p3的源指针:"<< p3.get()<< endl;
shared ptr<student>p4 = make shared<student>("哪吒");
p3= p4;//"哪吒"的引用计数+1,"东海龙王"的引用计数-1
cout<<"p4的源指针:"<< p4.get()<< endl;
cout <<"p3的源指针:"<<p3.get()<< endl;
cout<<"东海龙王引用计数:"<< p1.use_count()<< endl;
cout <<"哪吒引用计数:"<<p4.use_count()<<endl;return 0;
}//swap具体用法
int main()
{
shared ptr<student>p1(new student("白龙马"));
shared ptr<student>p2(new student("牛魔王"));
p1->show();
p2->show();
p1.swap(p2);
cout<<"交换后-"<<endl;
p1->show();
p2->show();
return 0;
}//uniqu具体用法
int main()
{
shared_ptr<student>p1(new student("金角大王"));
shared_ptr<student>p2= p1; //金角大王,引用计数为2
shared_ptr<student>p3(new student("银角大王"));//银角大王,引用计数为1if(p1.unique())
cout<<"金角大王,引用计数唯一"<<endl;
else
cout<<"金角大王,引用计数不唯一"<<endl;if(p3.unique())
cout<<"银角大王,引用计数唯一"<<end1;
else
cout<<"银角大王,引用计数不唯一"<< endl ;
return 0;
}
4.作为函数参数
传值和传引用都可以,建议传引用
void test01(shared_ptr<student>p)//传值
{
p->show();
}
void test02(shared ptr<student>& p)//传引用
{
p->show();
}
int main()
{
shared_ptr<Student>p = make_shared<Student>("菩提老祖");
test01(p);
test02(p);
return 0;
}
5.作为函数返回值
可以返回shared_ptr的值,但不能返回局部变量的引用
shared ptr<Student>test01()
{
shared ptr<Student>p = make shared<Student>("太上老君");
return p;
}
shared ptr<student>& test02()//不能返回局部变量的引用
{
shared ptr<student>p = make shared<student>("铁扇公主");//局部变量
return p;//返回引用,程序崩溃(未定义行为)
}
int main()
{
auto p1=test01();
auto p2=test02();
p1->show();
p2->show();
return 0:
}
除此之外,shared_ptr还可以作为数组,类继承等,还可以自定义删除器(方法同unique_ptr在上一篇中有讲述,这里不再赘述)
6.shared_ptr的不安全情况
shared ptr是线程安全的,
shared ptr不是绝对安全,如果程序中调用exit()退出,全局的shared_ptr可以释放,但局部的shared ptr无法释放。
二.weak_ptr
weak_ptr是对shared_ptr所管理对象的一种弱引用,它不会控制对象的生命周期。它
指向一个由shared_ptr管理的对象,但不拥有该对象。当最后一个shared_ptr被销毁时
无论是否还有weak_ptr指向该对象,对象都会被删除。weak_ptr主要用于解决shared_ptr
之间的循环引用问题。
shared_ptr之间的循环引用问题
在C++中,shared_ptr是用来管理动态分配对象的生命周期的智能指针,它使用引用计数来确保当最后一个指向对象的shared_ptr被销毁时,对象本身也会被销毁。然而,当shared_ptr之间形成循环引用时,这个问题就变得复杂了。
循环引用发生在两个或多个shared_ptr相互引用对方所管理的对象时。由于每个shared_ptr都持有一个引用计数,只要引用计数不为零,它所指向的对象就不会被销毁因此,即使程序的其他部分不再需要这些对象,它们也不会被释放,从而导致内存泄漏。
例如:
class A;
class B;
class A{
public:
A(){
m_a = new int[10];//动态创建10个元素
cout << "A()"<< endl;
}
~A()
{
delete[]m_a;//释放内存
cout <<“~A()”<< endl;
}
shared_ptr<B>b_ptr;
private:
int* m_a;//模拟动态创建的内存
};
class B{
public:
shared_ptr<A>a_ptr;
B(){
m_b= new int[10];//动态创建10个元素
cout <<"B()"<< endl;
}
~B()
{
delete[]m b;//释放内存
cout <<“~B()”<< endl;
}
private:
int* m_b;
};
int main(){auto a =std::make shared<A>();
auto b= std::make_shared<B>();
a->b_ptr = b;
b->a_ptr = a;//a和b的引用计数都是 2,因为它们相互引用
//当a和b离开作用域时,它们的引用计数不会降到 0
//因此,A和B的实例不会被销毁,导致内存泄漏
return 0;}
weak_ptr的作用
为了解决shared_ptr之间的循环引用问题,C++标准库提供了weak_ptr。weak_ptr是对shared_ptr所管理对象的一种弱引用,它不会增加对象的引用计数。因此,当最后一个shared ptr被销毁时,即使还有weak ptr指向该对象,对象也会被销毁。修改上面的例子,使用weak_ptr来打破循环引用
修改上面的代码,解决这个问题:
class A;
class B;
class A{
public:
A(){
m_a = new int[10];//动态创建10个元素
cout << "A()"<< endl;
}
~A()
{
delete[]m_a;//释放内存
cout <<“~A()”<< endl;
}
weak_ptr<B>b_ptr;
private:
int* m_a;//模拟动态创建的内存
};
class B{
public:
shared_ptr<A>a_ptr;
B(){
m_b= new int[10];//动态创建10个元素
cout <<"B()"<< endl;
}
~B()
{
delete[]m b;//释放内存
cout <<“~B()”<< endl;
}
private:
int* m_b;
};
int main(){auto a =std::make shared<A>();
auto b= std::make_shared<B>();
a->b_ptr = b;
b->a_ptr = a;//a和b的引用计数都是 2,因为它们相互引用
//当a和b离开作用域时,它们的引用计数不会降到 0
//因此,A和B的实例不会被销毁,导致内存泄漏
return 0;}
使用weak_ptr时需要小心,因为它不保证所指向的对象仍然存在。在访问weak_ptr所指向的对象之前,通常通过调用lock函数测试其有效性,并将其升级为shared_ptr。如果lock()返回空指针,则意味着原始对象已经被销毁
int main()
{
weak_ptr<int> wp;
//创建一个块作用域的智能指针
{
shared_ptr<int>sp(new int(10));
wp = sp;
shared_ptr<int>tmp1= wp.lock();//获取资源
if(tmp1 != NULL)
cout<< *tmp1<< endl;
else
cout<<"资源已经过期"<< endl;
}//块结束,sp自动释放shared_ptr<int>tmp2= wp.lock();//重新获取资源
if(tmp2 != NULL)
cout << *tmp2<< endl;
else
cout<<"资源已经过期" <<endl;return(0);
}