C++11新特性(总结)

3、C++11新特性

3.1、RAII 和 NRV
1、RAII

​ RAII(Resource Acquisition Is Initialization ,资源获得即初始化),是一种利用对象生命周期来控制程序资源的简单技术。在对象构造时获得资源,接着控制对资源的访问在对象的生命周期内始终有效,最后在对象析构时释放资源。因为C++的语言机制保证了,当一个对象创建的时候,自动调用构造函数,当对象超出作用域的时候会自动调用析构函数。所以,在RAII的指导下,我们应该使用类来管理资源,将资源和对象的生命周期绑定。这样的好处在于:

  • 不需要显示的释放资源
  • 对象所管理的资源在其生命周期内始终有效
2、NRV

3.2、智能指针

​ 智能指针包含在头文件 < memory >中,标准命名std空间下,有auto_ptr(已经被弃用)、shared_ptr、weak_ptr、unique_ptr。智能指针就是模拟指针动作的类,一般智能指针都会重载 **-> **和 *** **,主要作用时管理动态内存的释放。

​ 智能指针是一个,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源。

1、智能指针解决的问题
  • 处理资源泄漏;有指针的时候,忘记释放已经不使用的内存,从而导致内存泄漏;

  • 处理空悬指针;虽然释放了申请的内存,但是ptr会变成空悬指针(野指针),会指向垃圾内存,因此需要将内存释放后的指针置空(ptr = nullptr;)

  • 比较隐晦的由异常造成的资源泄漏;new创建对象后因为发生异常而忘记调用delete。

2、shared_ptr

​ 多个指针指向相同的对象,采用引用计数的方式解决赋值与拷贝问题,每个shared_ptr的拷贝都指向同一块内存,每次拷贝内部引用计数+1,每次析构内部引用计数-1,为0时自动删除所指向的堆内存。其内部的引用计数是线程安全的,但是对象读取时需要加锁

​ 智能指针对象中引用计数是多个智能指针对象共享的,两个线程中的引用计数同时++或者–,这个操作不是原子的,举个例子,应用计数原来是1,++了两次,可能还是2。这样引用计数就错乱了,会导致资源未释放或者程序崩溃的问题,所以其实智能指针是加锁了的,也就是说引用计数的操作是线程安全的。

​ 声明: template < class T > class shared_ptr;

1)注意事项:
  • 不能将指针直接赋值给一个智能指针,一个是类,一个是指针,不能直接shared_ptr < int > p = new int;
  • 避免循环引用,会导致内存泄漏,会在weak_ptr得到完善;
  • 管理数组指针时,需要指定Delete以使用delete[] 操作符销毁内存,shared_ptr并没有针对数组的特化版本;
  • 不能把一个原生指针交给两个智能指针对象管理,其他智能指针也是这样。
2)实现
  • 智能指针将一个计数器与类指向的对象相关联,引用计数器跟踪共有多少个类对象共享同一指针。
  • 每次创建类的新对象时,初始化指针并将引用计数置为1。
  • 当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数。
  • 对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数。
  • 调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)
3)代码
template <typename T>
class sharedPointer
{
private:
    class Implement
    {
    public:
        Implement(T* p) : mPointer(p), mRefs(1) {}
        ~Implement() { delete mPointer; }

        T* mPointer;
        size_t mRefs;
    };
    Implement* mImplPtr;
public:
    explicit sharedPointer(T *p) : mImplPtr (new Implement(p)) {} //explicit不能发生隐式类型转换,就是参数类型不匹配,就会进行隐式转换
    ~sharedPointer() { decrease(); } //计数递减
    sharedPointer(const sharedPointer& other) : mImplPtr(other.mImplPtr) { increase(); } //计数递增

    sharedPointer operator = (const sharedPointer& other)
    {
        if (mImplPtr != other.mImplPtr) //避免自赋值
        {
            decrease();
            mImplPtr = other.mImplPtr;
            increase();
        }
        return *this;
    }

    T* operator -> () const
    {
        return mImplPtr->mPointer;
    }

    T* operator *() const
    {
        return *(mImplPtr->mPointer);
    }

private:
    void decrease()
    {
        if (--mImplPtr->mRefs) == 0) 
            delete mImplPtr;
    }
    void increase()
    {
        ++(mImplPtr->mRefs);
    }
};
3、make_shared

​ **make_shared:**是一种安全分配和使用动态内存的方法,主要功能是在动态内存中分配一个对象并且使用它,返回此对象的shared_ptr。

make_shared生成shared_ptr的优点和缺点:

1)效率更高,原始的 new 表达式分配对象, 然后传递给 shared_ptr (也就是使用 shared_ptr 的构造函数) 的话, shared_ptr 的实现没有办法选择, 而只能单独的分配控制块,但是make_shared内存分配,可以一次性完成,减少了内存分配的次数,所以效率高;

auto p = new widget();
shared_ptr sp1{ p }, sp2{ sp1 };

auto sp1 = make_shared(), sp2{ sp1 };

2)异常安全。比如说可能连续构造两个对象,然后再分配shared_ptr,那我构造一个对象发生可能异常,这个对象就泄漏了,就是说有可能造成shared_ptr 没有立即获得裸指针。这种情况就推荐使用make_shared来代替。

void F(const std::shared_ptr<Lhs>& lhs, const std::shared_ptr<Rhs>& rhs) { /* ... */ }
F(std::shared_ptr<Lhs>(new Lhs("foo")),std::shared_ptr<Rhs>(new Rhs("bar")));

F(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));

3)构造函数是保护或者私有的,不能使用make_shared

4)make_shared对象的内存可能没有办法及时回收。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值