由于裸指针存在不少问题,主要是下面这些:ios
难以区分指向的是单个对象仍是一个数组;c++
使用完指针以后没法判断是否应该销毁指针,由于没法判断指针是否“拥有”指向的对象;数组
在已经肯定须要销毁指针的状况下,也没法肯定是用delete关键字删除,仍是有其余特殊的销毁机制,例如经过将指针传入某个特定的销毁函数来销毁指针;函数
即使已经肯定了销毁指针的方法,因为1的缘由,仍然没法肯定究竟是用delete(销毁单个对象)仍是delete[](销毁一个数组);指针
假设上述的问题都解决了,也很难保证在代码的全部路径中(分支结构,异常致使的跳转),有且仅有一次销毁指针操做;任何一条路径遗漏均可能致使内存泄露,而销毁屡次则会致使未定义行为;c++11
理论上没有方法来分辨一个指针是否处于悬挂状态;日志
智能指针主要是下面几个,c++11以后就引入到标准库成为语言新特性了(以前是在boost库中)code
std::auto_ptr, std::unique_ptr对象
std::shared_ptr, std::weak_ptr内存
上述四个智能指针类型中,auto_ptr是c++ 98遗留的关键字,已经不建议使用,auto_ptr的功能均可以由unique_ptr更加高效的作到;本文主要先讲一讲unique_ptr;
unique_ptr在要表达“专属全部权”的语义时使用,即unique_ptr指针永远“拥有”其指向的对象,因此unique_ptr是一个move-only类型,一个unique_ptr指针是没法被复制的,只能将“全部权”在两个unique_ptr指针之间转移,转移完成后源unique_ptr将被设为null;
定义一个unique_ptr并指向一块动态内存:
std::unique_ptr<int> pInt(nullptr); pInt.reset(new int(1)); std::cout << *pInt << "\n"; //解引用操做与裸指针相同,打印的结果为1
unique_ptr一个经常使用的场景是在使用工厂模式的时候,在工厂方法中返回一个unique_ptr类型的指针,例如
#pragma once //investment.h #include <assert.h> #include <memory> #include <iostream> #include <functional> //类定义 class Investment { public: virtual ~Investment() { std::cout << "investment destoryed\n"; } }; void makeLogEntry(Investment* pInv) { std::cout << "deleting investment on " << pInv << "\n"; } class Stock : public Investment { public: Stock() { std::cout << "make an invesetment on stock\n"; } virtual ~Stock() { std::cout << "a stock investment destoryed,"; } }; class Bond : public Investment { public: Bond() { std::cout << "make an investmentt on bond\n"; } virtual ~Bond() { std::cout << "a bond investment destroyed,"; } }; class RealEstate : public Investment { public: RealEstate() { std::cout << "make an investmentt on RealEstate\n"; } virtual ~RealEstate() { std::cout << "a RealEstatend investment destroyed,"; } }; void deleteAndLog(Investment* pInv) { makeLogEntry(pInv); delete pInv; } template<typename T, typename... Ts> static auto makeInvestment(Ts&&... params) { auto delInvmt = [](Investment* pInv) { makeLogEntry(pInv); delete pInv; }; typedef std::unique_ptr<Investment, decltype(delInvmt)> InvestmentPtr; std::cout << sizeof(InvestmentPtr) << "\n"; InvestmentPtr pInv(nullptr, delInvmt); pInv.reset(new T(std::forward<Ts>(params)...));//不能直接将裸指针赋值给一个unique_ptr,要使用reset return pInv; }
客户端的调用方法
auto pInvestment = InvestmentMaker::makeInvestment<Stock>();
unique_ptr默认的销毁方式是经过对unique_ptr中的裸指针进行delete操做,可是也能够在声明的时候指定销毁函数,在上面的代码中,经过lambda表达式置顶了一个打印日志函数,要在销毁指针的时候会打印日志,
template<typename T, typename... Ts> static auto makeInvestment(Ts&&... params) { auto delInvmt = [](Investment* pInv) { makeLogEntry(pInv); delete pInv; }; typedef std::unique_ptr<Investment, decltype(delInvmt)> InvestmentPtr; InvestmentPtr pInv(nullptr, delInvmt); pInv.reset(new T(std::forward<Ts>(params)...));//不能直接将裸指针赋值给一个unique_ptr,要使用reset return pInv; }
这里使用了c++ 14支持auto函数返回类型推导的特性,若是是c++11的话就须要把lambda表达式写到makeInvestment方法外面了。
在使用默认delete操做的时候,unique_ptr的内存size与裸指针一致,而使用自定义销毁方式的时候,unique_ptr的size取决于自定义销毁的方式:
在使用函数指针的时候,unique_ptr增长一个或两个字长;
使用函数对象的时候,unique_ptr的size取决于函数对象中的语句数量;
使用不带capture的lambda表达式时,不增长size
void deleteAndLog(Investment* pInv) { makeLogEntry(pInv); delete pInv; } template<typename T, typename... Ts> static auto makeInvestment(Ts&&... params) { auto delInvmt = [](Investment* pInv) { makeLogEntry(pInv); delete pInv; }; typedef std::unique_ptr<Investment, decltype(delInvmt)> InvestmentPtr; std::cout << sizeof(InvestmentPtr) << "\n"; //lambda表达式不增长size, size为4 typedef std::unique_ptr<Investment, decltype(&deleteAndLog)> FunPtrInvestmentPtr; std::cout << sizeof(FunPtrInvestmentPtr) << "\n"; //函数指针增长1一个字长, size为8 std::function<void(Investment*)> funcionObj = deleteAndLog; typedef std::unique_ptr<Investment, decltype(funcionObj)> FunObjInvestmentPtr; std::cout << sizeof(FunObjInvestmentPtr) << "\n"; //函数对象增长大小取决于函数体的语句数量,这里的size为48 //... }
unique_ptr是c++11以后用来表示专属全部权的一种智能指针,除了上面讲到的这些特性以外,另外还有一个很是方便的特性就是能够无缝地转换成shared_ptr,以上面的代码为例子,在调用工厂方法的时候,能够直接赋值给一个shared_ptr指针
std::shared_ptr<Investment> pInvestment = makeInvestment<Stock>();
shared_ptr将在下一篇博客中来说
unique_ptr是一种内存占用少、速度快、move-only的一种智能指针,用来管理“专属全部”语义下的动态资源;
默认的销毁方式是经过delete,但也能够自定义销毁方式,根据自定义销毁方式的不一样幅度增长unique_ptr的size
将unique_ptr转化为shared_ptr是很是容易的;