智能指针就是帮我们C++程序员管理动态分配的内存的,它会帮助我们自动释放new出来的内存,从而避免内存泄漏!
内存泄漏的例子:
C++中内存泄漏的几种情况_Denny_Zoom的博客-CSDN博客_内存泄漏的几种情况
C++造成内存泄漏的原因汇总:_盖伊福克斯的博客-CSDN博客_c++内存泄漏
c++内存泄露(一):理解内存泄漏及内存泄漏常见情况_invisible_sky的博客-CSDN博客_c++ 内存泄露
一 auto_ptr
auto_ptr以前是用在C98中,C++11被抛弃,头文件一般用来作为独占指针
auto_ptr被赋值或者拷贝后,失去对原指针的管理
auto_ptr不能管理数组指针,因为auto_ptr的内部实现中,析构函数中删除对象使用delete而不是delete[],释放内存的时候仅释放了数组的第一个元素的空间,会造成内存泄漏。
auto_ptr不能作为容器对象,因为STL容器中的元素经常要支持拷贝,赋值等操作。
代码示例:
// 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的处理更加灵活。
二 unique_ptr
C++11中用来替代auto_ptr
拷贝构造和赋值运算符被禁用,不能进行拷贝构造和赋值运算
虽然禁用了拷贝构造和赋值运算符,但unique_ptr
可以作为返回值,用于从某个函数中返回动态申请内存的所有权,本质上是移动拷贝,就是使用std:move()
函数,将所有权转移。
代码示例:
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无法通过赋值的形式创建内存对象,即:
这种方式也是错误的。
提前释放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方法 可以直接删除原始指针,并且重置为空。
我们可以通过转移所有权的方式,来使另一个指针拥有原先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指向的地址。
三 shared_ptr
多个指针可以指向相同的对象,调用release()
计数-1
,计数0
时资源释放
use_count()
查计数
reset()
放弃内部所有权
share_ptr
多次引用同一数据会导致内存多次释放
循环引用会导致死锁,
引用计数不是原子操作
shared_ptr
可以实现多个对象共同托管一个指针,这个是unique_ptr
做不到的。当曾经的托管对象接触对其托管时,系统会自动执行delete p
释放内存,这样保证了内存不会泄漏。
代码示例:
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
1.解决两个share_ptr
互相引用产生死锁,计数永远降不到0,没办法进行资源释放,造成内存泄漏的问题。
2.使用时配合share_ptr
使用,把其中一个share_ptr
更换为weak_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;
}
此时他们之前存在相互引用,但是已经方法已经返回了还没稀释
运行结果:
解释原因可以参考:智能指针(三):weak_ptr浅析_AlbertS的博客-CSDN博客_weakptr
解释了无法析构的原因,这里简单介绍一下。主要是相互引用时,导致引用记数无法为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;
}
运行结果:
可以明显的看出,已经被析构了。