美文网首页
智能指针

智能指针

作者: 小松qxs | 来源:发表于2019-01-24 15:43 被阅读0次

原理:

智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放。方便管理堆内存。
智能指针是利用RAII(资源获取即初始化)技术对普通指针进行封装,使智能指针实质是一个对象,行为表现的像一个指针。

常用的智能指针:

1、shared_ptr:
基于引用计数的智能指针。可随意赋值,当内存引用计数为0时内存被释放。支持多个指针指向相同的对象。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁。

初始化:智能指针是模板类,可以指定类型,传入指针通过构造函数初始化。也可以使用make_shared函数初始化。不能将指针直接赋值给一个智能指针,因为智能指针是类,不能用指针赋值。
例如:std::shared_ptr<int> p4 = new int(1);写法错误
拷贝和赋值:拷贝使对象的引用计数加1,赋值使原对象(左侧)引用计数减1,新对象(右侧)引用计数加1,当计数为0时,自动释放内存。
get函数:获取原始指针,不要用一个原始指针初始化多个shared_ptr,否则会造成二次释放同一内存。

#include <iostream>
#include <memory>

int main() {
    {
        int a = 10;
        std::shared_ptr<int> ptra = std::make_shared<int>(a);
        std::shared_ptr<int> ptra2(ptra); //copy
        std::cout << ptra.use_count() << std::endl;  // 2

        int b = 20;
        int *pb = &a;
        //std::shared_ptr<int> ptrb = pb;  // error 不能用指针赋值类
        std::shared_ptr<int> ptrb = std::make_shared<int>(b);
        ptra2 = ptrb; // assign ptra2由ptra位置变为指向ptrb
        pb = ptrb.get(); // 获取原始指针

        std::cout << ptra.use_count() << std::endl;  // 1
        std::cout << ptrb.use_count() << std::endl;  // 2
    }
}

2、unique_ptr:
“唯一”拥有所指对象。同一时刻只能有一个unique_ptr指向给定对象,禁止拷贝语义、只有移动语义。在出现异常的情况下,动态资源能得到释放。

创建:通过构造函数指定
重新指定:reset方法
释放所有权:release方法
移动语义转移所有权:std::move

#include <iostream>
#include <memory>

int main() {
    {
        std::unique_ptr<int> uptr(new int(10));  //绑定动态对象
        //std::unique_ptr<int> uptr2 = uptr;  //不能赋值,编译出错
        //std::unique_ptr<int> uptr2(uptr);  //不能拷贝
        std::unique_ptr<int> uptr2 = std::move(uptr); //转移所有权,uptr无效指针
        uptr2.release(); //释放所有权
    }
    /超过uptr的作用域,內存释放
}

3、weak_ptr:
弱引用,只引用,不计数,没有重载operator*和->,对shared_ptr资源观测。针对shared_ptr互相引用形成环(循环引用),两个指针指向的内存都无法释放的问题。需要手动打破循环引用或使用weak_ptr。

构造:从一个shared_ptr或者另一个weak_ptr对象构造。
观测资源的引用计数:成员函数use_count()。
被观测的资源是否存在:成员函数expired(),功能等价于use_count()==0,但更快。
从被观测的shared_ptr获得一个可用的shared_ptr对象:成员函数lock(), 操作资源。当expired()==true的时候,lock()函数返回一个存储空指针的shared_ptr。
注意:如果一块内存被shared_ptr和weak_ptr同时引用,所有shared_ptr析构后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前需要检查weak_ptr是否为空指针。

#include <iostream>
#include <memory>

int main() {
    {
        std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);
        std::cout << sh_ptr.use_count() << std::endl;  // 1

        std::weak_ptr<int> wp(sh_ptr);
        std::cout << wp.use_count() << std::endl;  // 1

        if(!wp.expired()){
            std::shared_ptr<int> sh_ptr2 = wp.lock(); //get another shared_ptr
            *sh_ptr = 100;
            std::cout << wp.use_count() << std::endl;  // 2
        }
    }
    //delete memory
}

循环引用例子如下:
错误例子:

#include <iostream>
#include <memory>

class Child;
class Parent;

class Parent {
private:
    std::shared_ptr<Child> ChildPtr;
public:
    void setChild(std::shared_ptr<Child> child) {
        this->ChildPtr = child;
    }
    void doSomething() {
        if (this->ChildPtr.use_count()) {

        }
    }
    ~Parent() {
    }
};

class Child {
private:
    std::shared_ptr<Parent> ParentPtr;
public:
    void setPartent(std::shared_ptr<Parent> parent) {
        this->ParentPtr = parent;
    }
    void doSomething() {
        if (this->ParentPtr.use_count()) {

        }
    }
    ~Child() {
    }
};

int main() {
    std::weak_ptr<Parent> wpp;
    std::weak_ptr<Child> wpc;
    {  // {}代表作用域,出作用域以下p和c应该被销毁
        std::shared_ptr<Parent> p(new Parent);   // 1
        std::shared_ptr<Child> c(new Child);   // 1
        p->setChild(c);   // 2
        c->setPartent(p);   // 2
        wpp = p;
        wpc = c;
        std::cout << p.use_count() << std::endl; // 2
        std::cout << c.use_count() << std::endl; // 2
    }  // 出作用域时对象p和c没销毁,每个count只减少一次
    std::cout << wpp.use_count() << std::endl;  // 1
    std::cout << wpc.use_count() << std::endl;  // 1
    return 0;
} 

正确做法:

#include <iostream>
#include <memory>

class Child;
class Parent;

class Parent {
private:
    //std::shared_ptr<Child> ChildPtr;
    std::weak_ptr<Child> ChildPtr;
public:
    void setChild(std::shared_ptr<Child> child) {
        this->ChildPtr = child;
    }
    void doSomething() {
        //new shared_ptr
        if (this->ChildPtr.lock()) {

        }
    }
    ~Parent() {
    }
};

class Child {
private:
    std::shared_ptr<Parent> ParentPtr;
public:
    void setPartent(std::shared_ptr<Parent> parent) {
        this->ParentPtr = parent;
    }
    void doSomething() {
        if (this->ParentPtr.use_count()) {

        }
    }
    ~Child() {
    }
};

int main() {
    std::weak_ptr<Parent> wpp;
    std::weak_ptr<Child> wpc;
    {
        std::shared_ptr<Parent> p(new Parent); // 1
        std::shared_ptr<Child> c(new Child); // 1
        p->setChild(c); // c = 1
        c->setPartent(p); // p = 2
        wpp = p;
        wpc = c;
        std::cout << p.use_count() << std::endl; // 2
        std::cout << c.use_count() << std::endl; // 1
    } // c只有1,1-->0,销毁c对象时调用child的析构函数,会对p销毁一次,而p对象本身会销毁一次,所以也是0
    std::cout << wpp.use_count() << std::endl;  // 0
    std::cout << wpc.use_count() << std::endl;  // 0
    return 0;
}

智能指针实现:

#include <iostream>
#include <memory>

template<typename T>
class SmartPointer {
private:
    T* _ptr;
    size_t* _count;
public:
    SmartPointer(T* ptr = nullptr) : _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)++;
        }
    }

    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;
            }
        }

        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<int> sp(new int(10));
        SmartPointer<int> sp2(sp);
        SmartPointer<int> sp3(new int(20));
        std::cout << sp.use_count() << std::endl;
        std::cout << sp3.use_count() << std::endl;
        sp2 = sp3;
        std::cout << sp.use_count() << std::endl;
        std::cout << sp3.use_count() << std::endl;
    }
    //delete operator
}

相关文章

  • 目录

    智能指针(1) 智能指针(2) 智能指针(3) 智能指针之使用 容器 - vector(1) 容器 - vecto...

  • 智能指针到Android引用计数

    智能指针 LightRefBase RefBaseStrongPointerWeakPointer 智能指针 这是...

  • C++面试重点再梳理

    智能指针 请讲一下智能指针原理,并实现一个简单的智能指针 智能指针其实不是一个指针。它是一个用来帮助我们管理指针的...

  • C++研发工程师笔试题/面试题(1-10)

    1. (1) 简述智能指针的原理;(2)c++中常用的智能指针有哪些?(3)实现一个简单的智能指针。 简述智能指针...

  • 第十六章 string类和标准模板库(2)智能指针模板类

    (二)智能指针模板类 智能指针是行为类似指针的类对象,但这种对象还有其他便于管理内存的功能。 1.使用智能指针 (...

  • Rust for cpp devs - 智能指针

    与 cpp 类似,Rust 也有智能指针。Rust 中的智能指针与引用最大的不同是,智能指针 own 内存,而引用...

  • C++ 引用计数技术及智能指针的简单实现

    1.智能指针是什么 简单来说,智能指针是一个类,它对普通指针进行封装,使智能指针类对象具有普通指针类型一样的操作。...

  • 智能指针

    1. 什么是智能指针? 智能指针是行为类似于指针的类对象,但这种对象还有其他功能。 2. 为什么设计智能指针? 引...

  • chrome中智能指针使用

    chrom中智能指针的概述和作用 chrome中智能指针的用法和总结 包含如下四种智能指针:scoped_ptr ...

  • c++智能指针用法

    智能指针是什么 智能指针是c++中有四个智能指针:auto_ptr、shared_ptr、weak_ptr、uni...

网友评论

      本文标题:智能指针

      本文链接:https://www.haomeiwen.com/subject/gikyjqtx.html