您的当前位置:首页智能指针介绍

智能指针介绍

来源:小侦探旅游网
智能指针介绍

⼀、智能指针的作⽤:

在C++中,动态内存的管理是⽤⼀对运算符完成的:new和delete,new:在动态内存中为对象分配⼀块空间并返回⼀个指向该对象的指针,delete:指向⼀个动态独享的指针,销毁对象,并释放与之关联的内存。

动态内存管理经常会出现三种问题:

1、申请之后忘记释放内存,会造成内存泄漏;

2、另⼀种是尚有指针引⽤内存的情况下就释放了它,就会产⽣引⽤⾮法内存的指针。3、还有⼀种是内存的⼆次释放,即对同⼀个指针进⾏两次 free() 操作,可能导致程序崩溃

智能指针的作⽤就是解决上述三种可能出现的问题,指针指针的使⽤效率不会⽐⼀般的指针⾼,但是它胜在更安全、更稳定

⼆、智能指针的本质

智能指针的实质是⼀个类对象,它是利⽤模板类对⼀般的指针进⾏封装,在类内的构造函数实现对指针的初始化,并在析构函数⾥编写delete语句删除指针指向的内存空间。这样在程序过期的时候,对象会被删除,内存会被释放,实现指针的安全使⽤。

三、智能指针的类型和使⽤

智能指针是在C++11版本之后提供,包含在头⽂件#include中,智能指针有四种类型,分别是shared_ptr、unique_ptr、auto_ptr、weak_ptr,这⾥只介绍前两种

每种指针都有不同的使⽤范围,unique_ptr指针优于其它两种类型,除⾮对象需要共享时⽤shared_ptr。如果你没有打算在多个线程之间来共享资源的话,那么就请使⽤unique_ptr。

1、shared_ptr

shared_ptr可以将多个指针指向相同的对象(共享)。shared_ptr使⽤引⽤计数,每⼀个shared_ptr的拷贝都指向相同的内存。每使⽤他⼀次,对象的引⽤计数

加1,每析构⼀次,对象的引⽤计数减1,减为0时,⾃动删除所指向的堆内存。

shared_ptr内部的引⽤计数是线程安全的,但是对象的读取需要加锁。

shared_ptr的初始化

智能指针是个模板类,可以指定类型,传⼊指针通过构造函数初始化。也可以使⽤make_shared函数初始化。不能将指针直接赋值给⼀个智能指针,⼀

个是类,⼀个是指针。例如std::shared_ptr p4 = new int(1);的写法是错误的

shared_ptr的拷贝和赋值

拷贝使得对象的引⽤计数增加1,赋值使得原始对象引⽤计数减1,当计数为0时,⾃动释放内存。后来指向的对象引⽤计数加1,指向后来的对象。

  shared_ptr传参的过程就是内存的拷贝,使得对象的引⽤计数增加1

  

#include #include using namespace std;

shared_ptr func(shared_ptr ps){

(*ps)++;

cout<<\"ps.use_count()=\"<int main() { int n=100; //赋值

shared_ptrp=make_shared(n); //拷贝

shared_ptrq (p);

cout<<\"q.use_count()=\"<cout<<\"p=.use_count()\"<get函数获取原始指针

我们前⾯说过,shared_ptr的本质是⼀个模板类,这⾥通过get()函数可以获取它的原始指针

//创建共享指针同时赋值 ClassA是⼀个类

shared_ptr b = make_shared(100);//b是⼀个类,c是类指针ClassA* c=b.get()

注意不要⽤⼀个原始指针初始化多个shared_ptr,否则会造成⼆次释放同⼀内存

2、unique_ptr

unique_ptr\"唯⼀\"拥有其所指对象,同⼀时刻只能有⼀个unique_ptr指向给定对象(通过禁⽌拷贝语义、只有移动语义来实现)

unique_ptr指针本⾝的⽣命周期:从unique_ptr指针创建时开始,直到离开作⽤域。离开作⽤域时,若其指向对象,则将其所指对象销毁(默认使⽤delete操作符,⽤户可指定其他操作)。

unique_ptr指针与其所指对象的关系:在智能指针⽣命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造

函数指定、

通过reset⽅法重新指定、通过release⽅法释放所有权、通过移动语义转移所有权。

#include #include

int main() { {

int a = 10;

//动态绑定对象

std::unique_ptr p (new int(a)); std::cout<<*p<//move转移所有权,转移之后指针p就变得⽆效了 std::unique_ptr q=std::move(p); std::cout<<*q<//release释放所有权,释放之后指针q变的⽆效 q.release(); }}

3、weak_ptr()

四、智能指针的设计和实现

上⾯说过,智能指针的实质就是⼀个类对象,它是利⽤模板类对⼀般的指针进⾏封装,在类内的构造函数实现对指针的初始化,增加引⽤计数,并在析构函数⾥编写delete语句删除指针指向的内存空间。

1、智能指针类将⼀个计数器与类指向的对象相关联,引⽤计数跟踪该类有多少个对象共享同⼀指针。每次创建类的新对象时,初始化指针并将引⽤计数置为1;

2、拷贝构造函数:当对象作为另⼀对象的副本⽽创建时,拷贝指针并增加与之相应的引⽤计数;

3、对⼀个对象进⾏赋值时,赋值操作符减少左操作数所指对象的引⽤计数(原始指针,如果引⽤计数为减⾄0,则删除对象),并增加右操作数所指对象的引⽤计数;

4、调⽤析构函数时,构造函数减少引⽤计数(如果引⽤计数减⾄0,则删除基础对象)。

智能指针就是模拟指针动作的类。所有的智能指针都会重载 =、-> 和 * 操作符。

⼀、shared_ptr()指针的实现

template < class T>class SmartPointer{

private:

//⼀般指针 T* _ptr; //计数

size_t* _count;public:

//将普通指针ptr封装成智能指针,ptr为⼀个普通指针 _ptr(ptr)是构造函数形参列表的初始化赋值操作

SmartPointer(T* ptr = nullptr) : _ptr(ptr) {

//如果被初始化的指针ptr不为空指针 if (_ptr) {

_count = new size_t(1); }

else {

_count = new size_t(0); }

}

//指针指针的拷贝构造

SmartPointer(const SmartPointer& ptr) {

if (this != &ptr) {

this->_ptr = ptr._ptr;

this->_count = ptr._count; (*this->_count)++; }

}

//赋值,重载=运算符 *this=ptr

SmartPointer& operator=(const SmartPointer& ptr) {

//判断是不是⾃⼰给⾃⼰赋值 if (this->_ptr == ptr._ptr) { return *this;

}

//等号左边的对象的 原始指针 计数减⼀ if (this->_ptr) {

(*this->_count)--; if (this->_count == 0) {

delete this->_ptr; delete this->_count; }

}

//复制之后,等号右边的对象引⽤计数加⼀,要注意的是_ptr和_count都是指针 this->_ptr = ptr._ptr;

this->_count = ptr._count; (*this->_count)++; return *this; }

//重载*

T& operator*() {

assert(this->_ptr == nullptr); return *(this->_ptr);

}

//重载->

T* operator->() {

assert(this->_ptr == nullptr); return this->_ptr; }

//析构函数

~SmartPointer() {

(*this->_count)--;

if (*this->_count == 0) { delete this->_ptr; delete this->_count; }

}

//计数器

size_t use_count() {

return *this->_count; }};int main()

{ {

//新建指针

SmartPointer sp(new int(10)); //拷贝

SmartPointer sp2(sp);

//拷贝之后,sp2和sp的引⽤计数都加⼀

std::cout << sp2.use_count() << std::endl;//2 std::cout << sp.use_count() << std::endl;//2 //新建

SmartPointer sp3(new int(20));

std::cout << sp2.use_count() << std::endl;//2 std::cout << sp3.use_count() << std::endl;//1 //赋值

sp2 = sp3;

//赋值之后,sp3的引⽤计数加⼀,sp2的引⽤计数不变,但是sp2的原始指针的引⽤计数减⼀ std::cout << sp.use_count() << std::endl;//1 std::cout << sp2.use_count() << std::endl;//2 std::cout << sp3.use_count() << std::endl;//2 }

system(\"pause\"); return 0;}

⼆、unique_ptr()指针的实现

我们可以在类中把拷贝构造函数和拷贝赋值声明为private,这样就不可以对指针指向进⾏拷贝了,也就不能产⽣指向同⼀个对象的指针。因为把拷贝构造函数和赋值操作符都声明为delete或private,这样每⼀个智能指针要指向⼀个对象时只能是指向⼀个新实例化的对象⽽不能通过“=”或者拷贝去指向前⾯已经创建了的对象,

例如“unique ptr=&aa”,这⾥调⽤了赋值操作符这是不可以的

封装成unique_ptr()的类包括如下成员函数:构造函数析构函数

reset():释放源资源,指向新资源release():返回资源,放弃对资源的管理

get():返回资源,只是供外部使⽤,依然管理资源operator bool (): 是否持有资源operator * ()operator -> ()

拷贝构造函数,禁⽤,不⽀持拷贝赋值函数,禁⽤,不⽀持

unique_ptr()指针是如何保证只有⼀个对象的引⽤?---------------------把拷贝构造函数和赋值操作符都声明为delete或private

#includeusing namespace std;templateclass UniquePtr{

private:

// 禁⽤拷贝构造

UniquePtr(const UniquePtr &) = delete; // 禁⽤拷贝赋值

UniquePtr& operator = (const UniquePtr &) = delete;

//封装的普通指针 T *_ptr;

//析构函数 void del() {

if (nullptr == _ptr) return; delete _ptr; _ptr = nullptr; }

public:

//构造函数

UniquePtr(T *ptr = NULL) : _ptr(ptr){ }

//析构

~UniquePtr() {

//释放内存私有化 del(); }

// 改变指针指向,先释放资源(如果持有), 再持有资源 void reset(T* ptr) {

del();

_ptr = ptr; }

// 释放指针,把当前指针的所有权转移给调⽤⽅ T* release() {

T* pTemp = _ptr; _ptr = nullptr; return pTemp; }

// 获取资源,调⽤⽅应该只使⽤不释放,否则会两次delete资源 T* get(){

return _ptr; }

// 是否持有资源

operator bool() const { return _ptr != nullptr; }

T& operator * (){ return *_ptr; }

T* operator -> (){ return _ptr; }};

int main(){ {

//创建指针并绑定对象

UniquePtr sp(new int(10)); //创建空指针

UniquePtr sp1; //绑定对象

sp1.reset(new int(11));

//输出指针内容

cout << *sp << \" \" << *sp1 << endl;

//把sp指针的所有权转移给另⼀个普通指针,但是使⽤普通指针的时候不要释放, //否则会多次调⽤delete内存泄漏,慎⽤这个⽅法 int* pp = sp.get();

cout << *pp << \" \" << *sp << endl; //转移sp指针

int* p = sp.release();

cout << *p << endl;//sp为空指针 //新建

//UniquePtr sp3(new int(20)); //禁⽤拷贝构造

//UniquePtr sp2(sp); //禁⽤赋值 //sp = sp3;

}

system(\"pause\"); return 0;}