智能指针unique_ptr(effective modern c++笔记)

为何要用智能指针呢?

由于裸指针存在不少问题,主要是下面这些:ios

  1. 难以区分指向的是单个对象仍是一个数组;c++

  2. 使用完指针以后没法判断是否应该销毁指针,由于没法判断指针是否“拥有”指向的对象;数组

  3. 在已经肯定须要销毁指针的状况下,也没法肯定是用delete关键字删除,仍是有其余特殊的销毁机制,例如经过将指针传入某个特定的销毁函数来销毁指针;函数

  4. 即使已经肯定了销毁指针的方法,因为1的缘由,仍然没法肯定究竟是用delete(销毁单个对象)仍是delete[](销毁一个数组);指针

  5. 假设上述的问题都解决了,也很难保证在代码的全部路径中(分支结构,异常致使的跳转),有且仅有一次销毁指针操做;任何一条路径遗漏均可能致使内存泄露,而销毁屡次则会致使未定义行为;c++11

  6. 理论上没有方法来分辨一个指针是否处于悬挂状态;日志

智能指针

智能指针主要是下面几个,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总结

  • unique_ptr是一种内存占用少、速度快、move-only的一种智能指针,用来管理“专属全部”语义下的动态资源;

  • 默认的销毁方式是经过delete,但也能够自定义销毁方式,根据自定义销毁方式的不一样幅度增长unique_ptr的size

  • 将unique_ptr转化为shared_ptr是很是容易的;

相关文章
相关标签/搜索