笔试题遇到了弱引用,但是C++标准库是没有这个概念的,学了智能指针但是没有听说过弱引用,因此总结一下两者
学习视频链接来自B站
https://www.bilibili.com/video/BV1gV4y1G7fH?p=2&vd_source=fa4ef8f26ae084f9b5f70a5f87e9e41b
智能指针
C++的对象本质上都需要调用delete来释放,当代码多起来时,总会有忘记手动delete的时候。因此C++提供了智能指针方便程序员。
unique_ptr
使用前需要导入memory头文件,unique_ptr只能单独管理一个对象,因此它的拷贝构造和=运算符被禁用,所以也不能进行值传递,nullptr也重载解引用运算,可以像普通指针一样通过*来访问
#include <iostream>
#include <memory>
using namespace std;
class A {
public:
string m_Name;
A() { cout << "这是A类的默认构造函数" << endl; }
A(string name)
{
this->m_Name = name;
cout << "这是A类的有参构造函数" << endl;
}
~A()
{
cout << "string = " << m_Name << "这是A类的析构函数" << endl;
}
};
int main()
{
A* p = new A("Tom");
//unique_ptr指针管理普通指针,unique_ptr指针本身也有自己的地址,第一种初始化方法
unique_ptr<A> uPtr1(p);
//推荐使用这种方法初始化一个智能指针
unique_ptr<A> uPtr2(new A("Jerry"));
//C++14语法
unique_ptr<A> uPtr3 = make_unique<A>("Jack");
return 0;
}
注:unique_ptr编译器上做了特殊的处理
1.将一个临时的unique_ptr赋值给另一个时(局部变量),编译器是允许这么做的;若unique_ptr已经管理了一个指针,那么它会先释放原本管理的指针,再去管理这个临时的unique_ptr,若这个变量长期存在(全局变量),那么这个操作是不允许的。原本的重载运算符=已经被禁用了,依然能接受赋值操作,非常神奇
**2.用nullptr给unique_ptr赋值将对象释放,空的unique_ptr == nullptr **
第九点贴图
shared_ptr
大体上与前一种智能指针一致,但是一个是独享,一个是共享,因此没有禁用拷贝构造和重载=
共享不是赋值,shared_ptr管理的原始指针只有一份
特别注意
指向资源的引用计数多一个,计数值就 + 1,指向资源的引用计数少一个,计数值就 - 1
3 4同unique_ptr
注意:
unique_ptr可以转换为shared_ptr,但是反过来不行
#include <iostream>
#include <memory>
using namespace std;
class A {
public:
string m_Name;
A() { cout << "这是A类的默认构造函数" << endl; }
A(string name)
{
this->m_Name = name;
cout << "这是A类的有参构造函数" << endl;
}
~A()
{
cout << "string = " << m_Name << "这是A类的析构函数" << endl;
}
};
void test01()
{
A* p = new A("Tom");
//unique_ptr指针管理普通指针,unique_ptr指针本身也有自己的地址,第一种初始化方法
unique_ptr<A> uPtr1(p);
//推荐使用这种方法初始化一个智能指针
unique_ptr<A> uPtr2(new A("Jerry"));
//C++14语法
unique_ptr<A> uPtr3 = make_unique<A>("Jack");
cout << "解引用运算符返回对象的属性," << "A类的名字为:" << (*uPtr2).m_Name << endl;
cout << "get方法返回管理的裸指针," << "A类的名字为:" << uPtr2.get()->m_Name << endl;
}
void test02()
{
A* p = new A("Tom");
//shared_ptr推荐使用这种方式初始化
shared_ptr<A> sPtr = make_shared<A>("Jack");
cout << "解引用运算符返回对象的属性," << "A类的名字为:" << (*sPtr).m_Name << endl;
cout << "shared_ptr的引用计数为:" << sPtr.use_count() << endl; //shared_ptr可以使用这个API返回引用计数值,实际一般不关心具体的值
}
int main()
{
// test01();
test02();
return 0;
}
智能指针删除器
share_ptr自定义删除器
unique_ptr自定义删除器
较为麻烦
weak_ptr 与弱引用
弱引用概念
AI是这么解释的
"弱引用"在计算机科学中是一个概念,尤其在内存管理和垃圾收集(Garbage Collection)的上下文中。弱引用与强引用相对。当一个对象被强引用时,只要这个引用存在,垃圾收集器就不会回收这个对象。相反,当对象被弱引用时,垃圾收集器会忽略这个引用,并在适当的时候回收这个对象,即使这个弱引用还存在。
弱引用的主要目的是防止内存泄漏,尤其是在需要引用对象但又不想阻止其被垃圾收集器回收的情况下。例如,在缓存系统中,当缓存对象被弱引用时,如果缓存大小超过了限制,垃圾收集器可以回收这些对象,从而避免内存溢出。
更简单的说法
弱引用是一种特殊的引用类型,它允许你引用一个对象,但不会增加该对象的引用计数。这意味着弱引用不会“拥有”对象,也不会阻止对象被垃圾回收器回收。当对象被回收时,弱引用会自动失效,再次访问它会导致错误或异常。
弱引用的主要目的是解决循环引用的问题。在C++中,如果你有两个对象相互引用,即使它们在其他地方都没有被引用,它们也不会被垃圾回收器回收,因为它们相互引用形成了一个“环”。弱引用可以打破这个环,让垃圾回收器能够回收对象。
weak_ptr
没有重载运算符"->"和解引用运算符
如果对方没死,那么自己不能死,最后谁也没死成
例子如下
class A {
public:
string m_Name;
shared_ptr<B> p;
A() { cout << "这是A类的默认构造函数" << endl; }
A(string name)
{
this->m_Name = name;
cout << "这是A类的有参构造函数" << endl;
}
~A()
{
cout << "string = " << m_Name << "这是A类的析构函数" << endl;
}
};
class B {
public:
string m_Name;
shared_ptr<A> p;
~B()
{
cout << "析构函数已经被调用" << endl;
}
};
void test03()
{
shared_ptr<A> pa = make_shared<A>();
shared_ptr<B> pb = make_shared<B>();
pa->p = pb;
pb->p = pa;
}
int main()
{
// test01();
// test02();
test03();
return 0;
}
解决方法,将一个类的shared_ptr改成weak_ptr
#include <iostream>
#include <memory>
using namespace std;
class A;
class B {
public:
string m_Name;
weak_ptr<A> p;
B(string name)
{
this->m_Name = name;
cout << "这是B类的有参构造函数" << endl;
}
~B()
{
cout << "B类的析构函数已经被调用" << endl;
}
};
class A {
public:
string m_Name;
shared_ptr<B> p;
A() { cout << "这是A类的默认构造函数" << endl; }
A(string name)
{
this->m_Name = name;
cout << "这是A类的有参构造函数" << endl;
}
~A()
{
cout << "string = " << m_Name << "这是A类的析构函数" << endl;
}
};
void test01()
{
A* p = new A("Tom");
//unique_ptr指针管理普通指针,unique_ptr指针本身也有自己的地址,第一种初始化方法
unique_ptr<A> uPtr1(p);
//推荐使用这种方法初始化一个智能指针
unique_ptr<A> uPtr2(new A("Jerry"));
//C++14语法
unique_ptr<A> uPtr3 = make_unique<A>("Jack");
cout << "解引用运算符返回对象的属性," << "A类的名字为:" << (*uPtr2).m_Name << endl;
cout << "get方法返回管理的裸指针," << "A类的名字为:" << uPtr2.get()->m_Name << endl;
}
void test02()
{
A* p = new A("Tom");
//shared_ptr推荐使用这种方式初始化
shared_ptr<A> sPtr = make_shared<A>("Jack");
cout << "解引用运算符返回对象的属性," << "A类的名字为:" << (*sPtr).m_Name << endl;
cout << "shared_ptr的引用计数为:" << sPtr.use_count() << endl; //shared_ptr可以使用这个API返回引用计数值,实际一般不关心具体的值
}
void test03()
{
shared_ptr<A> pa = make_shared<A>("Jack");
shared_ptr<B> pb = make_shared<B>("Tom");
pa->p = pb;
pb->p = pa;
}
int main()
{
// test01();
// test02();
test03();
return 0;
}
关键,一般互相配合使用在多线程程序中
升级的同时加锁pa,pb都是weak_ptr,m_p未指向对方类型的shared_ptr