智能指针其实是一个类模板,它与普通指针的区别在于他会自己释放内存空间。常见的智能指针有三种:unique_ptr、shared_ptr、weak_ptr。他们都在头文件<memory>中。
注:下面的普通指针就是裸指针。
目录
unique_ptr:
unique_ptr的创建:
unique_ptr的常用函数:
unique_ptr可能犯的错:
shared_ptr:
shared_ptr的创建:
shared_ptr的常用函数:
shared_ptr可能犯的错:
weak_ptr:
weak_ptr的创建:
weak_ptr的常用函数:
unique_ptr:
unique_ptr的创建:
unique_ptr是一种独占形的指针,即不能有两个独占性指针指向同一内存。它不允许复制,只允许移动(move),常用有三种创建方法,没有直接创建指针数组的,但可以自己自定义数据类型。
//第一种
unique_ptr<int> ptr1(new int(2)); //new一个内存空间给他
cout << ptr1 << " " << *ptr1 << endl;
//第二种
//unique_ptr<int> ptr2(ptr1); //报错,不可直接拷贝(浅拷贝)
unique_ptr<int> ptr2(move(ptr1)); //通过move将ptr1的内存给ptr1.
cout << ptr2 << " " << *ptr2 << endl;
cout << ptr1 << endl; //此时会发现其指向的地址已经被置空了
//cout << *ptr1 << endl; //不可再调用*ptr1,因为它指向的内存已经移动给ptr2了,会报异常。
//第三种
unique_ptr<int> ptr3;
ptr3 = make_unique<int>(3); //调用make_unique函数。
cout << ptr3 << " " << *ptr3 << endl;
//第四种,创建一个指针数组
//自定义数据类型
class shuzu {
private:
int* p;
int size;
public:
shuzu() = default; //显式构造构造函数
shuzu(int k) { //记得重写构造函数创建空间
this->size = k;
this->p = new int[k];
}
~shuzu() { //析构函数也要记得
this->size = 0;
delete[] p;
p = nullptr; //要置空,防止野指针的出现
}
int get_p() {
if (p) { return 0; }
else { return p[0]; }
}
};
//创建指针数组
unique_ptr<shuzu> ptr4(new shuzu(4));
cout << ptr4 << " " << ptr4->get_p() << endl;
unique_ptr的常用函数:
unique_ptr有四个常用函数,一个是release,一个是reset,一个是get,还有一个swap。release可以将该指针置为空,然后返回一个普通指针指向该内存。reset有一个普通指针的参数,默认为空,会先销毁该指针指向的地址,再让该指针指向参数所指向地址。get会返回一个普通指针,可以让该指针与智能指针指向同一个地址。swap的参数是另一个unique_ptr,会交换两个指针所指向的地址。
unique_ptr<int> ptr3;
ptr3 = make_unique<int>(3);
cout << "ptr3 = " << ptr3 << " *ptr3 = " << *ptr3 << endl;
unique_ptr<int> ptr1(new int(2));
cout << "ptr1 = " << ptr1 << " *ptr1 = " << *ptr1 << endl;
int* p = ptr1.get(); //获取ptr1的地址
cout << "p= " << p<< " *p= " << *p<< endl;
cout << "ptr1 = " << ptr1 << " *ptr1 = " << *ptr1 << endl;
ptr1.swap(ptr3); //交换两指针指向地址
cout << "ptr3 = " << ptr3 << " *ptr3 = " << *ptr3 << endl;
cout << "ptr1 = " << ptr1 << " *ptr1 = " << *ptr1 << endl;
int* ptr5 = ptr3.release(); //因为智能指针是unique_ptr<int>,所以返回一个int*的指针
cout << "ptr5 = " << ptr5 << " *ptr5 = " << *ptr5 << endl;
//ptr3.release(); //将ptr3置空并释放内存
ptr1.reset(ptr5); //因为智能指针是unique_ptr<int>,所以参数是int*的指针
cout << "ptr5 = " << ptr3 << endl;
cout << "ptr1 = " << ptr1 << " *ptr1 = " << *ptr1 << endl;
ptr1.reset(); //释放ptr1指向的内存并置空
unique_ptr可能犯的错:
我们都知道,unique_ptr是独占类型的指针,但如果我们通过一个指针给两个unique_ptr类型的指针赋初值是可以成功的,而且这两个指针还指向同一地址,这就会导致unique_ptr变为非独占型,间接导致unique_ptr的reset操作异常以及其他依赖于该指针独占性的操作出问题。所以不能做以下操作:
int main() {
//成功创建指针并指向同一地址
int* trap = new int(4);
unique_ptr<int> ptr1(trap);
unique_ptr<int> ptr2(trap);
unique_ptr<int> ptr3(trap);
cout << "trap的地址为:" << trap << endl;
cout << "ptr1的地址为:" << ptr1 << endl;
cout << "ptr2的地址为:" << ptr2 << endl;
cout << "ptr3的地址为:" << ptr3 << endl;
//调用函数
ptr1.release(); //可以成功,但只是将ptr1这个指针置空
//ptr2.reset(); //无法成功,因为该内存不只被ptr2所拥有
return 0; //在执行完return 0 后会报错,因为无法销毁非独占的独占型指针。
}
shared_ptr:
shared_ptr的创建:
shared_ptr是一种共享性的智能指针(类模板),它允许移动和复制,每复制一次后,它的计数就会加一,当计数为0时就会销毁。常用的创建方法有:①使用new创建(不推荐,因为使用new会导致重复了被创建对象的键入,导致编译次数增加,代码膨胀,最终难以维护。)②使用make_shared,用法同make_unique。③直接复制另一个shared_ptr,会让这两个指针指向同一块内存。④使用move将一个shared_ptr指向的内存移动到另一个shared_ptr,被移动的指针会被置空。
//用法一,不推荐
shared_ptr<int> ptr2(new int(10));
cout << "ptr2的地址为:" << ptr2 << " ptr2的值为:" << *ptr2 << endl;
//用法二,使用make_shared,推荐
shared_ptr<int> ptr1(make_shared<int>(10));
cout << "ptr1的地址为:" << ptr1 << " ptr1的值为:" << *ptr1 << endl;
//直接复制
shared_ptr<int> ptr3 = ptr1;
cout << "ptr3的地址为:" << ptr3 << " ptr3的值为:" << *ptr3 << endl;
//将ptr1所指向的内存移动给ptr4
shared_ptr<int> ptr4(move(ptr1));
cout << "ptr4的地址为:" << ptr4 << " ptr4的值为:" << *ptr4 << endl;
//ptr1被移动,被置空了
cout << "ptr1的地址为:" << ptr1 << endl;
//ptr3不变
cout << "ptr3的地址为:" << ptr3 << " ptr3的值为:" << *ptr3 << endl;
shared_ptr的常用函数:
shared_ptr有几个常用函数:use_count、get、reset、unique、swap,其中use_count函数可以查看与该指针指向同一内存的智能指针数(也就是计数)。get同上,可以获取地址。reset也有两个使用方法,若没有参数,则会将该指针置空,然后计数减一,若计数为0,则销毁该内存;若存在参数,则参数是一个新开辟出的内存地址(不能是裸指针),先将计数减一,若计数为0,则销毁该内存,再将指针指向该地址。unique函数返回一个bool值,若该指针计数为1,则返回true,否则返回false。swap函数同unique_ptr的一样,都是交换两个指针指向的地址。
shared_ptr<int> ptr1(make_shared<int>(10));
cout << "ptr1的地址为:" << ptr1 << "ptr1的计数为:"<< ptr1.use_count() << " ptr1的值为:" << *ptr1 << endl;
//此时计数为1,unique返回true
cout << ptr1.unique() << endl;
shared_ptr<int> ptr3 = ptr1;
cout << "ptr3的地址为:" << ptr3 << "ptr3的计数为:" << ptr3.use_count() << " ptr3的值为:" << *ptr3 << endl;
//此时计数为2,unique返回fasle
cout << ptr1.unique() << endl;
//使用get获取地址
int* p = ptr1.get();
//reset无参数,ptr1指向的地址会被置空,计数-1.
ptr1.reset();
cout << "ptr1的地址为:" << ptr1 << "ptr1的计数为:" << ptr1.use_count() << endl;
//reset有参数,地址会指向新开辟出来的,值为4.
ptr1.reset(new int(4));
cout << "ptr1的地址为:" << ptr1 << "ptr1的计数为:" << ptr1.use_count() << " ptr1的值为:" << *ptr1 << endl;
//交换两指针
ptr1.swap(ptr3);
cout << "ptr1的地址为:" << ptr1 << "ptr1的计数为:" << ptr1.use_count() << ptr1 << " ptr1的值为:" << *ptr1 << endl;
cout << "ptr3的地址为:" << ptr3 << "ptr3的计数为:" << ptr3.use_count() << " ptr3的值为:" << *ptr3 << endl;
shared_ptr可能犯的错:
一:同unique_ptr一样,shared_ptr也不能用一个裸指针创建两个shared_ptr,因为你在创建的时候机器会用同一个地址创建两个指针,他们因为刚被创建,计数都是一,等到删除的时候,比如删掉第一个,计数为0,机器以为这块内存无人使用了,想将他销毁,可第二个指针还在使用这块内存,造成错误的出现。
int main() {
int* p = new int(5);
shared_ptr<int> ptr1(p);
shared_ptr<int> ptr2(p);
cout << "ptr1的地址为:" << ptr1 << " ptr1的计数为:" << ptr1.use_count() << " ptr1的值为:" << *ptr1 << endl;
//系统以为ptr2指向的内存刚被创建,所以ptr2的计数也是1,但实际上是2.
cout << "ptr2的地址为:" << ptr2 << " ptr1的计数为:" << ptr2.use_count() << " ptr2的值为:" << *ptr2 << endl;
return 0; //在执行完return 0 后会报错,因为无法销毁指针。
}
二:shared_ptr的循环引用问题,本质是因为使用的shared_ptr的参数类型是一个带有shared_ptr的成员函数,而且这两个指向同一地址。如下方的a与a->a_这两个shared_ptr指向同一地址,当程序结束时,a销毁,a的use_count减一变为1,所以a所指向的内存不销毁,而该内存不销毁,a->a_就不会销毁(不调用析构函数),其use_count不会为0,该内存一直存在。
而使用weak_ptr,并不会让a的计数加一,所以a销毁时,计数为0,a.a_也会被成功销毁。
class A
{
public:
std::shared_ptr<A> a_;
//std::weak_ptr<int> a; //这个改成weak_ptr就没问题了
public:
A() {
std::cout << "construct A" << std::endl;
}
~A() {
std::cout << "destroy A" << std::endl;
}
};
int main() {
// *** circular reference *** ///
std::shared_ptr<A> a(new A()); // a count: 1
cout <<"a的地址为:" << a << " a的计数为:" << a.use_count() << endl;
a->a_ = a; // b count: 2
cout << "a->a_的地址为:" << a->a_ << " a->a_的计数为:" << a->a_.use_count() << endl;
cout << "a的地址为:" << a << " a的计数为:" << a.use_count() << endl;
return 0;
}
weak_ptr:
weak_ptr的创建:
weak_ptr一般不单独使用,它并未重载诸如 *、->等指针操作符,而是作为shared_ptr的补充(监视)。它的初始化需要用到shared_ptr的指针,当使用一个shared_ptr初始化weak_ptr时,该shared_ptr的计数并不会加一!!!!!所以他可以用来解决上面shared_ptr可能出现的第二个问题。
//创建
weak_ptr<int> wptr1;
//初始化
shared_ptr<int> ptr1(new int(3));
wptr1 = ptr1;
cout << "ptr1的地址为:" << ptr1 << " ptr1的计数为:" << ptr1.use_count() << endl; //计数为1,不是2哦
//可以通过复制创建
weak_ptr<int> wptr2(wptr1);
weak_ptr的常用函数:
weak_ptr有几个常用函数:lock、reset、use_count、swap、expired。lock函数会返回weak_ptr所绑定的shared_ptr对象,不推荐用来初始化新的shared_ptr,若无绑定对象则返回NULL。reset会给weak_ptr“解绑”,即该指针指向空(不影响它所绑定的shared_ptr)。use_count可以用来获取它绑定对象的计数值。swap用来交换两个指针绑定的对象。expired会返回一个bool值,若weak_ptr为空或者其绑定的shared_ptr为空,则返回true(为空返回true哦)。
weak_ptr<int> wptr1;
shared_ptr<int> ptr1(new int(3));
wptr1 = ptr1;
cout << "ptr1的地址为:" << ptr1 << endl; //计数为1,不是2哦
shared_ptr<int> ptr2(new int(5));
weak_ptr<int> wptr2(ptr2);
cout << "ptr2的地址为:" << ptr2 << endl;
//交换wptr1与wptr2的绑定对象。
wptr1.swap(wptr2);
cout << wptr1.lock() << endl; //返回ptr2的地址。
cout << wptr2.lock() << endl; //返回ptr1的地址。
//未解绑前
cout << wptr1.lock() << endl; //返回ptr2的地址(一个指向ptr2的shared_ptr<int>的指针)。
cout << wptr1.expired() << endl; //ptr2不为空,返回false
cout << wptr1.use_count() << endl; //计数为1
//解绑
wptr1.reset();
cout << wptr1.lock() << endl; //被解绑了,返回空
cout << wptr1.expired() << endl; //被解绑了,返回true
cout << wptr1.use_count() << endl; //计数为0,因为被解绑了