- 智能指针出现的原因:主要解决的是堆内存分配释放,忘记释放内存引发的内存泄漏问题,智能指针最主要的事情就是让程序员无需去注意内存释放,内存释放的问题放在智能指针内部进行处理。
- 智能指针有四种,包括
auto_ptr
,shared_ptr
,unique_ptr
,weak_ptr
,其中auto_ptr
已经被C++11废弃,所以放在最后介绍。
unique_ptr
unique_ptr的几个重要特性:
- 1.两个unique_ptr不能指向一个对象,即unique_ptr不能共享它所管理的对象,这句话个人的理解的,unique_ptr无法赋值指针到另一个指针,让他们指向同一个对象。
简单的示例代码:
class A{
public:
A(){
cout << "demo"<< endl;
}
void B(){
cout << "测试" << endl;
}
};
void unique_ptr_use()
{
unique_ptr<A> A_ptr(new A());
A_ptr->B();
cout << A_ptr << endl;
unique_ptr<A> demo = A_ptr; //此时编译错误,无法通过赋值的方式传递指针地址,即与demo共享同一块内存
}
由上面代码可以看出,编译unique_ptr<A> demo = A_ptr
会报错,因为unique_ptr
是只能单独享有对对象的独有权。
另外,unique_ptr无法通过赋值的形式创建内存对象,即:
这种方式也是错误的。
- 2.提前释放unique_ptr对象可以通过
reset方法进行重置
示例代码:
void unique_ptr_use()
{
unique_ptr<A> A_ptr(new A());
A_ptr->B();
cout << A_ptr << endl;
A_ptr.reset();
cout << A_ptr << endl;
}
运行结果:
可以看出:
通过reset方法 可以直接删除原始指针,并且重置为空。
- 3.我们可以通过转移所有权的方式,来使另一个指针拥有原先unique_ptr的地址
示例代码:
void unique_ptr_use()
{
unique_ptr<A> A_ptr(new A());
A_ptr->B();
cout << A_ptr << endl;
unique_ptr<A>BB = std::move(A_ptr);
cout << A_ptr << endl;
cout << BB << endl;
}
运行结果:
通过std::move()
方法,将A_ptr的对象转移到BB上面,A_ptr指针为空,BB拥有A_ptr指向的地址。
-
release()
方法会释放原有的原始指针,但是不会删除原先的指针,删除原先的指针需要通过reset()
方法
示例代码:
void unique_ptr_use()
{
unique_ptr<A> A_ptr(new A());
A_ptr->B();
cout << A_ptr << endl;
A * nn = A_ptr.release();
cout << nn << endl;
cout << A_ptr << endl;
delete nn;
nn = nullptr;
cout << nn << endl;
}
运行结果:
- 知识点:使用
delete
删除指针,编译器只会释放原先指向的内存空间,不会释放指针本身,所以需要通过设置指针为空来删除指针本身!!! - 4.使用get方法 获取关联的指针对象
void unique_ptr_use()
{
unique_ptr<A> A_ptr(new A());
A_ptr->B();
A * m = A_ptr.get();
cout << "11---" << m << endl;
cout << A_ptr << endl;
A * nn = A_ptr.release();
cout << nn << endl;
cout << A_ptr << endl;
delete nn;
nn = nullptr;
cout << nn << endl;
}
运行结果:
shared_ptr
shared_ptr
可以实现多个对象共同托管一个指针,这个是unique_ptr
做不到的。当曾经的托管对象接触对其托管时,系统会自动执行delete p
释放内存,这样保证了内存不会泄漏。
-
shared_ptr
:可以查看赋值对象的个数,因为可以多个对象共同托管,所以个数需要获得,已经内置了一个use_count()
方法返回当前可以操作该指针的对象个数。 - 示例代码:
void shared_ptr_use()
{
shared_ptr<A> mm = make_shared<A>();
mm->B();
cout <<"mm value:" << mm << endl;
shared_ptr<A> ff(mm);
cout << "ff value:" << ff << endl;
cout << "个数为" << ff.use_count() << endl;
mm.reset();//重置
cout <<"now ff:"<< ff << endl;
cout << "个数为" << ff.use_count() << endl;
cout <<"now mm:"<< mm << endl;
}
- 运行结果:
weak_ptr
- 首先,
weak_ptr
不能直接定义一个对象类,需要配合shared_ptr
进行使用,通过shared_ptr
传指针地址给它。 -
weak_ptr
主要解决的一个问题是shared_ptr
相互引用无法释放内存的问题。 - 实例代码:
class B;
class A{
public:
A(){
cout << "A init!" << endl;
}
~A(){
cout << "A Destroy" << endl;
}
void set_quote_ptr(shared_ptr<B>mm)
{
m_b = mm;
}
private:
shared_ptr<B> m_b;
};
class B{
public:
B(){
cout << "A init!" << endl;
}
~B(){
cout << "A Destroy" << endl;
}
void set_quote_ptr(shared_ptr<A>mm)
{
m_a = mm;
}
private:
shared_ptr<A> m_a;
};
void test_ptr()
{
shared_ptr<A>a_ptr = make_shared<A>();
shared_ptr<B>b_ptr = make_shared<B>();
a_ptr->set_quote_ptr(b_ptr);
b_ptr->set_quote_ptr(a_ptr);
cout << "a count" << a_ptr.use_count() << endl;
cout << "b count" << b_ptr.use_count() << endl;
}
int main()
{
test_ptr();
system("pause");
return 0;
}
此时他们之前存在相互引用,但是已经方法已经返回了还没稀释
运行结果:
解释原因可以参考:https://blog.csdn.net/albertsh/article/details/82286999#commentBox
:解释了无法析构的原因,这里简单介绍一下。主要是相互引用时,导致引用记数无法为0;所以无法调用函数进行析构。
- 接下来解决的方案,是使用
weak_ptr
来替代其中一个shared_ptr
,因为这样赋值的话,不会引发计数变化,修改完的代码如下:
#include "stdafx.h"
#include <iostream>
#include <memory>
using namespace std;
class B;
class A{
public:
A(){
cout << "A init!" << endl;
}
~A(){
cout << "A Destroy" << endl;
}
void set_quote_ptr(shared_ptr<B>mm)
{
m_b = mm;
}
private:
weak_ptr<B> m_b;
};
class B{
public:
B(){
cout << "A init!" << endl;
}
~B(){
cout << "A Destroy" << endl;
}
void set_quote_ptr(shared_ptr<A>mm)
{
m_a = mm;
}
private:
weak_ptr<A> m_a;
};
void test_ptr()
{
shared_ptr<A>a_ptr = make_shared<A>();
shared_ptr<B>b_ptr = make_shared<B>();
a_ptr->set_quote_ptr(b_ptr);
b_ptr->set_quote_ptr(a_ptr);
cout << "a count" << a_ptr.use_count() << endl;
cout << "b count" << b_ptr.use_count() << endl;
}
int main()
{
test_ptr();
system("pause");
return 0;
}
运行结果:
可以明显的看出,已经被析构了。
- weak_ptr还有两个主要的方法
lock()
和expired()
,这边简单介绍一下 -
expired
主要判断赋值给他的shared_ptr
对象是否还在,在的话返回1,不在的话返回0 -
lock()
返回赋值它的shared_ptr
对象。
实例代码:
// Weak_Ptr_Use.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <memory>
using namespace std;
class B;
class A{
public:
A(){
cout << "A init!" << endl;
}
~A(){
cout << "A Destroy" << endl;
bool is_fired = m_b.expired();
cout << "endl:" << is_fired << endl;
}
void set_quote_ptr(shared_ptr<B>mm)
{
m_b = mm;
}
weak_ptr<B> m_b;
};
class B{
public:
B(){
cout << "B init!" << endl;
}
~B(){
cout << "B Destroy" << endl;
}
void set_quote_ptr(shared_ptr<A>mm)
{
m_a = mm;
}
void show()
{
cout << "调用B方法测试" << endl;
}
private:
weak_ptr<A> m_a;
};
void test_ptr()
{
shared_ptr<A>a_ptr = make_shared<A>();
shared_ptr<B>b_ptr = make_shared<B>();
a_ptr->set_quote_ptr(b_ptr);
b_ptr->set_quote_ptr(a_ptr);
weak_ptr<B> b_weak_ptr = b_ptr;
if (b_weak_ptr.expired())
{
b_weak_ptr.lock()->show();
}
cout << "a count" << a_ptr.use_count() << endl;
cout << "b count" << b_ptr.use_count() << endl;
}
int main()
{
test_ptr();
system("pause");
return 0;
}
运行结果:
auto_ptr
- 简单来说,auto_ptr和unique_ptr比较像,但是auto_ptr具备拷贝语义,unique_ptr只有转移所有权的方式
代码示例:
// Weak_Ptr_Use.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
A(){
cout << "A Init" << endl;
}
~A()
{
cout << "A Destory" << endl;
}
void show()
{
cout << "demo" << endl;
}
};
void test_auto_demo()
{
unique_ptr<A> mma(new A());
cout << mma << endl;
auto_ptr<A> A_PTR(new A());
auto_ptr<A>mm = A_PTR; //实质和std::move()语义一样。
cout << mm.get() << endl;
cout << A_PTR.get() << endl;
A_PTR->show();
}
int main()
{
test_auto_demo();
system("pause");
return 0;
}
运行结果:
造成在这个的原因是auto_ptr<A>mm = A_PTR; //实质和std::move()语义一样。
这边A_PTR已经把所有权转给mm,此时A_PTR为空指针,空指针必然报错。。
所以unique_ptr
比auto_ptr
更加安全,实质上个人觉得应该是编译器对unique_ptr
的处理更加灵活。
智能指针的应用场景