C++的标准模板库STL一共提供了四种智能指针,分别是auto_ptr、unique_ptr、shared_ptr、weak_ptr。它们的作用就是提我们管理动态内存分配。说简单点,就是怕你new/new[]了新的对象,然后忘记释放或者程序出现问题提前终止导致未运行到delete/delete[],造成内存泄露。
所以如果只能能自己释放内存就好了。。。这就是智能指针存在的意义。智能指针实际是个类对象,它管理一个指针(真正意义上的指针),在类对象失效(超出作用范围)时,调用类对象的析构函数时,自动释放它指向的对象。就和我们处理类的时候一样:
构建类,传入指针,析构类,delete指针。
1. auto_ptr指针
起初C++提供的是auto_ptr,但是这个不好用,最大的点就是这玩意可以完成相互赋值并且编译期不报错!!!
#include <iostream>
#include <string>
#include <memory>
int main()
{
std::auto_ptr<std::string> ptr1(new std::string("string"));
std::string *p = new std::string("123");
std::auto_ptr<std::string> ptr2(p);
ptr2 = ptr1;
std::cout << *ptr1;
}
这段代码是可以编译的,并且编译正确,但是运行到ptr1的输出时,程序崩溃。为啥呢?因为ptr2 = ptr1,ptr1指向的对象现在交给ptr2管理了,那么ptr1指向空值。
我当然可以在使用ptr1之前进行get判断:
if (!ptr1.get())
{
std::cout << "ptr1 is empty";
}
else
{
std::cout << *ptr1;
}
但是如果能不判断不就更好么,编译器在编辑期间就发现错误,及时提醒我有异常。这就是C++11引用新的智能指针的原因,C++11还保留着auto_ptr,为了可以更好的兼容,但是C++17已经删除了auto_ptr。
2. unique_ptr指针
我们来看下unique_ptr这个智能指针,它就可以解决auto_ptr的问题:
std::unique_ptr<std::string> ptr1(new std::string("string"));
std::string *p = new std::string("123");
std::unique_ptr<std::string> ptr2(p);
ptr2 = ptr1;//这句话编译报错
这个时候,编译器就会报错:
“std::unique_ptr<std::string,std::default_delete<_Ty>> &std::unique_ptr<_Ty,std::default_delete<_Ty>>::operator =(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)”: 尝试引用已删除的函数
如果我真的想用ptr2=ptr1呢?C++提供了move方式:
std::unique_ptr<std::string> ptr1(new std::string("string"));
std::string *p = new std::string("123");
std::unique_ptr<std::string> ptr2(p);
ptr2 = move(ptr1);
但是我强烈要求你不要这么干,特别是实际开发中,那么多代码,你把ptr1的权限move给ptr2了,那么此时ptr1又指向了空,谁再次调用ptr1时会崩溃(实际开发代码太多,调试特别慢):
std::cout << *ptr1;
虽然说unique_ptr对对象是独占的,不允许两个unique_ptr指向同一个对象,但是它可以指向临时右值,啥意思呢,如果右值是一个临时的、很快就会被销毁的unique_ptr(你都没机会去用它),那么编译器是允许的!例如:
std::unique_ptr<std::string> ptr3 = std::unique_ptr<std::string>(new std::string("test"));
std::cout << *ptr3 << std::endl;
程序输出ptr3,右值std::unique_ptrstd::string(new std::string(“test”))也是一个unique_ptr,但是它是一个临时右值,构造函数创建的临时对象在所有权被转移给ptr3以后就会被销毁,所以这段代码是正常的。
3. shared_ptr指针
如果你需要指向同一个对象,强烈建议你使用shared_ptr,这个指针就是为了你可以使用多个智能指针指向同一个对象。它内部有一个计数器,每有一个指针指向同一个对象,那么这个计数器加1,每有一个指针被释放,计数器减1,如果计数器为0,shared_ptr就会释放指向的对象。shared_ptr可以用use_count来查看有多少指针引用:
std::shared_ptr<std::string> ptr1(new std::string("string"));
std::string *p = new std::string("123");
std::shared_ptr<std::string> ptr2(p);
ptr2 = ptr1;
std::cout << ptr1.use_count() << std::endl;//引用数为2,ptr1和ptr2都指向了相同的对象string
ptr1.reset();//如果reset未指向任何新的对象,那么ptr1将指向空指针
std::cout << ptr2.use_count();//引用数为1,只有ptr2指向对象string
std::cout << ptr1.use_count() << std::endl;//ptr1指向空指针,引用数为0
std::cout << *ptr1 << std::endl;//会报错的,因为ptr1将指向空指针nullptr,这是测试,所以无所谓了,实际开发不要这么做
4. weak_ptr指针
weak_ptr弱指针,是配合shared_ptr使用的,最常见的错误就是两个shared_ptr指针相互引用,导致的无法释放,引起内存泄露(这样的例子网上很多):
class B包含class A类型shared_ptr,class A包含class B类型的shared_ptr,相互引用导致引用计数无法变为0
#include <iostream>
#include <memory>
class A;
class B;
class B {
private:
std::shared_ptr<A> ptr;
public:
void setPtr(std::shared_ptr<A> ptr) {
this->ptr = ptr;
}
~B() {
}
};
class A {
private:
std::shared_ptr<B> ptr2;
public:
void setPtr2(std::shared_ptr<B> ptr2) {
this->ptr2 = ptr2;
}
~A() {
}
};
int main() {
{
std::shared_ptr<B> b(new B);
std::shared_ptr<A> a(new A);
b->setPtr(a);
a->setPtr2(b);
std::cout << b.use_count() << std::endl; // 2
std::cout << a.use_count() << std::endl; // 2
}
return 0;
}
如何选用智能指针呢?
1.需要多个指针指向同一个对象,使用shared_ptr
2.只需要一个指针指向一个对象对象,用unique_ptr
3.会发生shared_ptr 的循环引用问题,用weak_ptr