????????简单介绍c++11的四种智能指针的用法。使用智能指针需要包含头文件#include<memory>。c++11提出了四种智能指针auto_ptr,unique_ptr,shared_ptr和weak_ptr。
1.auto_ptr:
????????auto_ptr是使用raii机制设计的智能指针,它在构造时申请动态内存,析构时释放动态内存。
使用方法如下:
#include<memory>
#include<iostream>
int main() {
std::auto_ptr<int32_t> p(new int32_t(10)); //创建时指定动态对象
std::auto_ptr<int32_t> q; //创建空智能指针
q.reset(new int32_t(9)); //绑定对象
std::cout << *p;
std::cout << *q.get(); //q.get()得到普通指针int32_t*
return 0;
}
????????但是由于浅拷贝的原因,当我们拷贝智能指针后,程序出现了两个指向相同对象的智能指针,此时如果程序退出那么一定会有一个智能指针在释放内存时内存已释放完毕,产生错误。
????????出于上面原因可以说几乎所有程序都不要使用auto_ptr。
2.unique_ptr:
? ? ? ? unique_ptr通过禁用拷贝构造函数和=复制语句来避免auto_ptr的情况,不过可以使用=来进行移动构造。用法如下:
#include<memory>
#include<iostream>
int main() {
std::unique_ptr<int32_t> p(new int32_t(10)); //创建时指定动态对象
std::unique_ptr<int32_t> q; //创建空智能指针
q.reset(new int32_t(9)); //绑定对象
std::cout << *p;
std::cout << *q.get(); //q.get()得到普通指针int32_t*
std::unique_ptr<int32_t> f;
f = std::move(q); //移动构造函数赋值
std::cout << *f;
//std::cout << *q; 此时该语句是错误的,因为q的内容已经转移给了f,q是空指针
return 0;
}
? ? ? ? 因此在使用auto_ptr的场景我们使用unique_ptr即可。
3.shared_ptr:
? ? ? ? 假如我们必须存在多个指向同一内存的智能指针,那么unique_ptr就不太合适了。此时我们可以使用shared_ptr,它通过引用计数的方式来记录同一块内存被指向的智能指针数,析构时会判断计数是否为1,如果是则释放,反之则减一且不释放。用法如下:
#include<memory>
#include<iostream>
int main() {
std::shared_ptr<int32_t> p(new int32_t(10)); //创建时指定动态对象
std::shared_ptr<int32_t> q = p; //赋值
std::shared_ptr<int32_t> f(new int32_t(9));
f.swap(p); //交换p和f的指向,但是等于p的q不会被交换
std::cout << *p << ' ' << *q << ' ' << *f; //输出9 10 10
return 0;
}
? ? ? ? 引用计数的实现是采用一个int*指针实现的,当拷贝时新指针的int*指针指向原来的计数块。
? ? ? ? shared_ptr会出现内存泄漏问题,即循环引用,试想如下情况:
std::shared_ptr<node> p(new node());
std::shared_ptr<node> q(new node());
p->ptr = q;
q->ptr = p;
? ? ? ? 此时p和q的计数都是2,当析构时,p和q各计数减一,然后此时发现还有计数,因此不调用析构函数释放ptr,此时没有任何方式能拿到ptr,产生内存泄漏。
? ? ? ? shared_ptr在读的时候是线程安全的,但是在写的时候不是。
4.weak_ptr:
????????weak_ptr和shared_ptr共同使用可以解决循环引用问题。weak_ptr赋值时引用计数不加一。它的用法和shared_ptr几乎一样,且可以和shared_ptr互相使用=拷贝。
|