您现在的位置是:首页 >学无止境 >“智能指针:C++中优雅的内存管理解决方案“网站首页学无止境
“智能指针:C++中优雅的内存管理解决方案“
前言
欢迎来到?小K?的?C++专栏?,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况,这是C和C++程序员的噩梦之一。本节将为大家带来解决办法—>智能指针
1、简介
✨智能指针是一个模板类,封装了裸指针,可以对指针进行安全的操作。
- 使用RAII特点,将对象生命周期使用栈来管理
- 智能指针区分了所有权,因此使用责任更为清晰
- 智能指针大量使用操作符重载和函数内联特点,调用成本和裸指针无差别
2、为什么要使用智能指针
✨一句话:智能指针就是帮我们C++程序员管理动态分配的内存的,它会帮助我们自动释放new出来的内存,从而避免内存泄漏!
✨内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况,这是C和C++程序员的噩梦之一。
3、unique_ptr
✨unique_ptr是一种定义在中的智能指针(smart pointer)。它持有对对象的独有权——两个unique_ptr不能指向一个对象,不能进行复制操作只能进行移动操作。
✨unique_ptr对象是管理的对象的唯一拥有者:因为当unique_ptr对象释放时会删除它们的托管对象,而不考虑其他指针是否仍然指向相同的对象,从而使指向那里的其他指针指向无效的位置。
✨unique_ptr对象复制了有限的指针功能,通过操作符*和->(用于单个对象)或操作符[](用于数组对象)提供对其托管对象的访问。出于安全考虑,它们不支持指针算术运算,只支持移动赋值(禁用复制赋值)。
✨下面是基本的使用方法
void testOne()
{
unique_ptr<int> p(new int(1234));
cout << *p << endl;
unique_ptr<int> p1;
//p1 = p; 错误,不能赋值和拷贝
//unique_ptr<int> p2(p);
p1 = move(p); //转交所有权
cout << *p1 << endl;
//cout << *p << endl; //错误,p已经失去了管理对象
unique_ptr<int> p3(new int(897));
p3.reset(p1.release());
cout << *p3 << endl;
}
✨需要手动写删除器的情况,一般是操作自定义类型(且注意unique_ptr
的删除器写法的,需要手动写入删除器类型)
class MM {
public:
~MM() {
cout << "调用析构函数成功" << endl;
}
};
void testTwo()
{
//unique_ptr删除器写法,需要手动写入删除器类型
unique_ptr<MM,void(*)(MM*&)> Pmm(new MM[4], [](MM*& pmm) {delete[] pmm; });
}
✨注意当函数参数为unique_ptr
类型时,需要使用引用,因为此类型是独享型,禁止拷贝
void print(unique_ptr<int>& p)
{
cout << *p << endl;
}
4、shared_ptr
✨unique_ptr是一个独享指针,同一时刻只能有一个unique_ptr指向一个对象,而shared_ptr是一个共享指针,同一时刻可以有多个shared_ptr指向同一对象,但会记录有多少个shared_ptr共同指向一个对象。这便是所谓的引用计数(reference counting)。
✨ 一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。
✨基本用法
void testOne()
{
shared_ptr<int> p1; //无参构造
if (!p1)
{
cout << "空的智能指针对象" << endl;
}
shared_ptr<int> p2(new int(1234));
//shared_ptr<int> p2 = new int(1234); 错误
shared_ptr<int> p3 = make_shared<int>(1234); //使用make_shared不支持创建数组
//怎么访问数据,直接把智能指针对象当做指针来用
cout << *p3 << endl;
//cout << p3[0] << endl; 错误,没有下标的使用方式
//获取管理对象原生指针
int* k = p3.get();
cout << *k << endl;
cout << k[0] << endl;
//注意:千万不要手动释放原生指针,否则会中断(释放两次)
//delete k;
cout << "管理对象数:" << p3.use_count() << endl;
shared_ptr<int> p4(p3);
cout << "管理对象数:" << p3.use_count() << endl;
}
✨vector管理普通指针的弊端
class MM
{
public:
MM(string name="",int age=0):name(name),age(age){}
void print()
{
cout << name << " " << age << endl;
}
~MM()
{
cout << "调用析构函数" << endl;
}
protected:
string name;
int age;
};
void testTwo()
{
cout <<"-----------------------------------" << endl;
shared_ptr<MM> p(new MM("name1", 19));
p->print();
/*****************vector弊端*****************/
vector<MM*> p1;
p1.push_back(new MM("name", 20));
//p1.pop_back();
p1.clear();
//可以看到pop和clear函数都不会自动调用析构函数---->当vector操作自定义类型指针的时候
/*******************************************/
//比较常用的用法--->工作中
vector<shared_ptr<MM>> arr;
shared_ptr<MM> p2(new MM("name"));
p2->print();
arr.push_back(p2);
}
上面代码中,当vector管理MM*
类型的时候,并不会自动调用析构函数,这时候我们只需要让vector管理shared_ptr
就好了
✨需要手动写删除器的情况,和unique_ptr
一样
void testThree()
{
cout << "-----------------------------------" << endl;
shared_ptr<int> king(new int[4] {1, 2, 3, 4});
int* pNum = king.get();
for (int i = 0; i < 4; i++)
{
cout << pNum[i] << " ";
}
cout << endl;
//当操作特殊类型(自定义类型,c语言文件的打开)的时候,需要手动写删除器
shared_ptr<MM> p1(new MM[4], [](MM*& pmm) {delete[]pmm; });
shared_ptr<FILE> pfile(fopen("1.txt", "w+"), [](FILE*& file) {free(file); });
}
✨当shared_ptr
当函数参数和返回值的时候,正常写就ok
void pprint(shared_ptr<MM>& mm) {
mm->print();
}
shared_ptr<int> returnpoint(int num) {
return shared_ptr<int>(new int(num));
}
5、weak_ptr
✨shared_ptr可以用来避免内存泄漏,可以自动释放内存。但是在使用中可能存在循环引用,使引用计数失效,从而导致内存泄漏的情况。如下代码所示:
class B;
class A {
public:
~A() {
cout << "A" << endl;
}
shared_ptr<B> a;
};
class B {
public:
~B() {
cout << "B" << endl;
}
shared_ptr<A> b;
};
void testOne()
{
shared_ptr<A> ao(new A);
shared_ptr<B> bo(new B);
ao->a = bo;
bo->b = ao;
}
我们发现上面的一段代码中,ao中的指针管理bo,bo中的指针管理ao,形成了循环管理,这时候就不会自动释放。这时候我们只需要把shared_ptr
改为weak_ptr
就ok了
✨基本处理
weak_ptr只能从shared_ptr或者已有的weak_ptr去构造,不能直接管理对象
不能直接访问管理的对象,访问数据要通过lock()函数去访问shared_ptr,然后再去访问数据
void testTwo()
{
shared_ptr<int> p(new int(1234));
cout << "count:" << p.use_count() << endl;
weak_ptr<int> w(p);
cout << "count:" << w.use_count() << endl;
weak_ptr<int> w2(w);
cout << "count:" << w2.use_count() << endl;
cout << *w.lock() << endl;
shared_ptr<int> k = w2.lock();
cout << *k << endl;
}