shared_ptr
共享指针的最显著特点就是计数器。
shared_ptr<string> p0;
cout << p0.use_count() << endl;
shared_ptr<string> p1(new string("hello"));
cout << p1.use_count() << endl;
shared_ptr<string> p2 = p1;
cout << p2.use_count() << endl;
system("pause");
return 0;
共享指针内部有一个use_count函数,能够返回该对象被引用的次数。
0
1
2
请按任意键继续. . .
shared_ptr允许多个指针指向同一个对象。
那么引用计数有啥用呢?
1,一旦一个对象通过调用new被分配出来,记录谁拥有这个对象是很重要的,因为其所有者要负责对它进行delete。但是对象所有者可以有多个,且所有权能够被传递,这就使得内存跟踪变得困难。引用计数可以跟踪对象所有权,并能够自动销毁对象
2,节省内存,提高程序运行效率。如果很多对象有相同的值,为这多个相同的值存储多个副本是很浪费空间的,所以最好做法是让左右对象都共享同一个值的实现。
shared_ptr的初始化方法
int *p = new int(3);
shared_ptr<int> sptr(p);//引用计数,1
shared_ptr<int> sptr2(new int(4));//引用计数,1
shared_ptr<int> sptr3 = sptr2;//拷贝构造,引用计数,2
shared_ptr<int> sptr4 = make_shared<int>(5);//引用计数,1
1,用指针初始化(不推荐)
2,用new+类型(初始值)初始化
3,拷贝构造初始化(引用计数会+1)
4,用make_shared<类型>(值)初始化
为什么1这个方法不推荐呢?因为容易出错
int main()
{
//foo();
int* p = new int(1);
shared_ptr<int> sptr(p);
cout << sptr.use_count() << endl;
{
shared_ptr<int> sptr2(p);
cout << sptr2.use_count() << endl;
}
system("pause");
return 0;
}
输出的两个use_count都是1,说明共享指针没有增加计数,也就是说,第二个共享指针不知道第一个共享指针的存在,如果第二个共享指针被析构了,那么第一个共享指针就会出问题。
weak_ptr
这个例子转自:https://zhuanlan.zhihu.com/p/73807983
为什么需要weak_ptr?是为了避免循环引用的问题。weak_ptr在赋值的时候,不会引起计数器增加。weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权,而在weak_ptr析构的时候也不会导致计数器减少。
如果要操作资源,则必须使用一个成员函数 lock(),从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。
那么什么是循环引用呢?
class TestB;
class TestA {
public:
TestA() { cout << "TestA()构造函数" << endl; }
void ReferTestB(shared_ptr<TestB> test_ptr)
{
m_TestB_ptr = test_ptr;
}
~TestA() { cout << "TestA()析构函数" << endl; }
private:
shared_ptr<TestB> m_TestB_ptr;
};
class TestB
{
public:
TestB() { cout << "TestB()构造函数" << endl; }
void ReferTestA(shared_ptr<TestA> test_ptr) {
m_TestA_ptr = test_ptr;
}
~TestB() { cout << "TestB()析构函数" << endl; }
private:
shared_ptr<TestA> m_TestA_ptr;
};
int main()
{
shared_ptr<TestA> a = make_shared<TestA>();
shared_ptr<TestB> b = make_shared<TestB>();
a->ReferTestB(b);
b->ReferTestA(a);
system("pause");
return 0;
}
运行结果是:只显示了构造函数,没有析构函数。
因此可以看出,在这段代码中,析构函数没有正常的被执行,发生了内存泄漏。
原因: a引用了b,b引用了a,在main退出之前,a和b的引用计数都是2,退出main函数吼,引用计数变成了1,也就是互相引用,最后计数器不会归0,析构函数也就不会被调用了。
这时候可以使用弱指针对上述代码进行修改:
#include <iostream>
#include <memory>
class TestB;
class TestA
{
public:
TestA()
{
std::cout << "TestA()" << std::endl;
}
void ReferTestB(std::shared_ptr<TestB> test_ptr)
{
m_TestB_Ptr = test_ptr;
}
void TestWork()
{
std::cout << "~TestA::TestWork()" << std::endl;
}
~TestA()
{
std::cout << "~TestA()" << std::endl;
}
private:
std::weak_ptr<TestB> m_TestB_Ptr;
};
class TestB
{
public:
TestB()
{
std::cout << "TestB()" << std::endl;
}
void ReferTestB(std::shared_ptr<TestA> test_ptr)
{
m_TestA_Ptr = test_ptr;
}
void TestWork()
{
std::cout << "~TestB::TestWork()" << std::endl;
}
~TestB()
{
把std::weak_ptr类型转换成std::shared_ptr类型
std::shared_ptr<TestA> tmp = m_TestA_Ptr.lock();
tmp->TestWork();
std::cout << "2 ref a:" << tmp.use_count() <<std::endl;
std::cout << "~TestB()" << std::endl;
}
std::weak_ptr<TestA> m_TestA_Ptr;
};
int main()
{
std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>();
std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>();
ptr_a->ReferTestB(ptr_b);
ptr_b->ReferTestB(ptr_a);
std::cout << "1 ref a:" << ptr_a.use_count()<<std::endl;
std::cout << "1 ref b:" << ptr_a.use_count()<<std::endl;
return 0;
}
运行结果:
TestA()
TestB()
1 ref a:1
1 ref b:1
~TestA::TestWork()
2 ref a:2
~TestB()
~TestA()
可以看到,weak_ptr在使用lock()函数后可以转化成shared_ptr,也就拥有了控制权。例子中,在析构函数~TestB中,声明一个shared_ptr的tmp,从A类的弱指针转化为共享指针,并调用A中的TestWork方法。
weak_ptr支持的调用
weak_ptr<T> w; //空weak_ptr可以指向类型为T的对象
weak_ptr<T> w(shared_ptr sp); //与sp指向相同对象的weak_ptr, T必须能转换为sp指向的类型
w = p; //p可以是shared_ptr或者weak_ptr,赋值后w和p共享对象
w.reset(); //weak_ptr置为空
w.use_count(); //与w共享对象的shared_ptr的计数
w.expired(); //w.use_count()为0则返回true,否则返回false
w.lock(); //w.expired()为true,返回空的shared_ptr;否则返回指向w的shared_ptr