C++智能指针详解

目录
  • 一. unique_ptr独占指针
    • 特点
    • 创建方式
    • 传递方式
    • 简单使用
    • 隐藏危险
  • 二. shared_ptr 计数指针
    • 特点
    • 传递方式
    • 隐藏危险
  • 三. weak_ptr

优缺点:

  • 智能指针由原始指针的封装,优点是可以自动分配内存,不用担心内存泄漏问题。
  • 用于解决独占/共享所有权指针的释放,传输等问题。
  • 但是没有原始指针方便。

一. unique_ptr独占指针

特点

都是围绕独占展开

特点一: 如其名,独占。也就是说同一个内存空间同时只能有一个指针来管理。

int* pi = new int(10); //利用传统指针在堆区开辟一个存放整数的区域
std::unique_ptr<int> u_pi_01{pi};//通过传统指针创建智能指针
std::unique_ptr<int> u_pi_02{pi};//有意让两个独占指针同时指向同一个内存区域

这么写编译器不会报错,但运行时会提示 error:double free detected in tcache 2

这也就印证了第一个特点,一个内存区域只能由一个指针管理。

特点二: 当指针超出作用域时,内存自动释放

//由于指针的本质也是变量,离开作用范围就会自动释放
//因此我们需要通过在外部创建变量来保存指针所保存的地址
int* external_pi_1;//用于存储传统指针的地址
int* external_pi_2;//用于存储智能指针的地址
 {
    int *pi = new int(10);//使用传统指针在堆区开辟内存存储整形
    external_pi_1 = pi;
    std::unique_ptr<int> u_pi{new int(10)};//使用智能指针在堆区开辟内存存储
    external_pi_2 = u_pi.get();
    std::cout << *external_pi_1 << std::endl;//输出10
    std::cout << *external_pi_2 << std::endl;//输出10
 }
std::cout << *external_pi_1 << std::endl;//输出10
std::cout << *external_pi_2 << std::endl;//输出0

可见传统指针在局部作用域中开辟的内存在外部同样可以访问,也就是说我们使用传统指针开辟内存之后在离开作用域时需要加上释放内存的操作,不然会造成内存泄漏。

而智能指针我们不需要手动释放内存,在离开作用域后会自动释放。

特点三:由于特点一,修改指针不可以copy ,只能Move(转移归属权)

std::unique_ptr<int> u_pi1 = std::make_unique<int>(10);
//std::unique_ptr<int> u_pi2 = u_pi1;//尝试用拷贝的方式共享内存,error:可别忘了这是独占指针
std::unique_ptr<int> u_pi2 = move(u_pi1);使用move方法转移内存拥有权。

也就是说,通过move函数,把指针u_pi1所指内存中的值掏空,然后安到指针u_pi2所指的内存上。

创建方式

方式一: 通过已有的传统指针创建

int* pi = new int(10); //使用传统指针在堆区开辟一个空间
std::unique_ptr<int> u_pi{pi};//利用创通指针创建智能指针

方式二: 通过new方法创建

std::unique_ptr<int> u_pi{new int(10)};

方式三: 通过std :: make_unique创建

std::unipue_ptr<int> u_pi = std::make_unique<int>(10);

传递方式

方式一: 通过move(),转移拥有权.

void show(std::unique_ptr<int> u_pi)
{
    std::cout<<*u_pi<<std::endl;
}
void test()
{
    std::unique_ptr<int> u_pi{new int(10)};
    show(move(u_pi)); //通过move转移拥有权
}

注意:将指针的拥有权转入函数中后,在原作用域指针将被释放,而该指针将在函数调用结束时释放。也就是说,将智能指针以move的形式传入函数后,在原作用域不能再使用该指针。

方式二: 通过引用传递

void show(const std::unique_ptr<int> &u_pi)//加cosnt 不是不能改变指向的值,不能改变指针的指向
{
    std::cout << *u_pi << std::endl;
    //u_pi.reset();加了const所以不能清空
}
void test()
{
    std::unique_ptr<int> u_pi{new int(10)};
    show(u_pi);
}

注意: 将指针以引用的方式传入函数,那么该指针在原作用域依然存活,并可以和所调用函数共同操作该内存空间数据。

方式三: 链式传递

std::unique_ptr<Person> get_unique(std::string str)
{
    std::unique_ptr<Person> u_pi{new Person(str)};
    return u_pi;
}
void test()
{
    get_unique("hua")->show();//链式
}

简单使用

  • 通过get()获取地址
  • 可以通过->调用成员函数
  • 可以通过*调用解引用
  • 通过reset()清空指针
class Person
{
public:
    Perosn(std::string name):m_name(name){};
    void show()
    {
        std::cout<<"name is "<<m_name<<std::endl;
    }
private:
    std::string m_name;
};
int main()
{
std::unique_ptr<Person> u_p{new Person("kimi")}; //用自定义类型创建
u_p->show();//可以通过->调用函数
(*u_p).show();//通过*解引用
std::cout<<u_p.get()<<std::endl;//通过get()获取地址
u_p.reset();//清空指针
return 0;
}

隐藏危险

用已有指针创建时,没有及时清空传统指针,导致同时有两个指针指向这块已经被“独占”的区域。

int* pi = new int(10);
std::unique_ptr<int> u_pi{pi};//使用传统指针创建,上式开辟的区域被独占
*pi = 20; //没有及时清空,依然可以通过独占指针以外的方式修改内存

二. shared_ptr 计数指针

特点

特点一: 可以通过copy共享内存。

std::shared_ptr<int> u_pi_1{new int(10)};
std::shared_ptr<int> u_pi_2 = u_pi_1;//通过复制拷贝

特点二: 通过use_count();来查看计数 ,copy 计数加一,销毁计数减一。

std::shanred_ptr<int> s_pi{new int(10)}; //s_pi.use_count() == 1
std::shanred_ptr<int> s_pi_copy = s_pi;  //s_pi.use_count() == 2
s_pi = nullptr;//清空指针                 //s_pi_copy.use_count() == 1

特点三: 无论多少指针,都同用一份数据,因而同一份数据的use_count()一致。

std::shared_ptr<int> s_pi{new int(10)}; //s_pi.use_count() == 1
std::shared_ptr<int> s_pi_2 = s_pi;     //s_pi.use_count() == 2
std::shared_ptr<int> s_pi_3 = s_pi_2;   //s_pi.use_count() == 3
s_pi_2 = bullptr; //清空2指针            //s_pi.use_count() == 2

传递方式

  • 本质不变,在函数调用中,因为本身支持复制操作,所以不用加move可以直接传递。
  • 并且在传递到函数中,use_count() 会增加,并在函数销毁时候还原。
  • 在函数中修改指向的值,在外部的指针指向的值也会改变。
  • 使用引用传递,则在传递到函数中时,计数不会增加。
void get_use_1(std::shared_ptr<int> s_pi)
{
    std::cout << s_pi.use_count() << std::endl;
}
void get_use_2(std::shared_ptr<int>& s_pi)
{
    std::cout << s_pi.use_count() << std::endl;
}
void test()
{
    std::shared_ptr<int> s_pi{new int(10)};
    std::cout << s_pi.use_count() << std::endl;
    get_use_1(s_pi);//在函数中计数会增加,但随着函数销毁,计数复原
    get_use_2(s_pi);//以引用方式传入,指针还是那个指针,计数不会增加
}

输出:1 2 1

隐藏危险

share_ptr带来的循环依赖问题

class Person
{
public:
    void set_friend(share_ptr<Person> p)

    _friend = p;
private:
    share_ptr<Person> _frient;
};
int main()
{
   share_ptr<Person> p1 = make_shared("P1");
   share_ptr<Person> p2 = make_shared("P2");
    p1->set_friend(P2);
    p2->set_friend(P1);//造成循环依赖,在main中的话,不会执行析构
}

解决:将_friend属性改为weak_ptr 。

三. weak_ptr

weak_ptr 是一个不需要所有权的指针,所以我们可以通过用weak_ptr来声明属性,解决循环依赖

class Person
{
public:
    void set_friend(share_ptr<Person> p)
    _friend = p;
private:
    weak_ptr<Person> _frient;//使用weak_ptr解决循环依赖
};
int main()
{
    share_ptr<Person> p1 = make_shared("P1");
    share_ptr<Person> p2 = make_shared("P2");
    p1->set_friend(P2);
    p2->set_friend(P1);
}

可以通过lock()来将weak_pte升级为shared_ptr;

std::weak_ptr<Person> w_pi{new Person("hua")};
std::shared_ptr <Person> s_pi2 = w_pi.lock();

到此这篇关于C++智能指针详解的文章就介绍到这了,更多相关C++智能指针内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 关于C++智能指针shared_ptr和unique_ptr能否互转问题

    C++中的智能指针最常用的是shared_ptr和unique_ptr,C++新手最常问的问题是我从一个函数中拿到unique_ptr,但要转成shared_ptr才能使用,要怎么转换?同理是否能将shared_ptr转换成unique_ptr? 我们先简单看看shared_ptr是什么. std::shared_ptr<Widget> a = std::make_shared<Widget>(); 这句代码会在栈中创建一个shared_ptr对象,其最基本的2个指针,一个指向在堆

  • 一起聊聊C++中的智能指针

    目录 一:背景 二:关键词解析 1. auto_ptr 2. auto_ptr 多引用问题 一:背景 我们知道 C++ 是手工管理内存的分配和释放,对应的操作符就是 new/delete 和 new[] / delete[], 这给了程序员极大的自由度也给了我们极高的门槛,弄不好就得内存泄露,比如下面的代码: void test() { int* i = new int(10); *i = 10; } int main() { test(); } 这段代码因为用了 new 而忘了 delete,

  • C++智能指针之shared_ptr的具体使用

    目录 std::shared_ptr概念 shared_ptr模板类 shared_ptr的构造和析构 shared_ptr赋值 make_shared 计数线程安全? enable_shared_from_this shared_ptr使用注意事项: 总结 std::shared_ptr概念 unique_ptr因为其局限性(独享所有权),一般很少用于多线程操作.在多线程操作的时候,既可以共享资源,又可以自动释放资源,这就引入了shared_ptr. shared_ptr为了支持跨线程访问,其

  • C++深入分析讲解智能指针

    目录 1.简介 2.unique_ptr指针(独占指针) 3.shared_ptr指针(共享所有权) 4.weak_ptr(辅助作用) 5.自实现初级版智能指针 6.总结 1.简介 程序运行时存在静态空间.栈和堆区,用堆来存储动态分配空间的对象即那些在程序运行时分配空间的对象,若该对象不再使用,我们必须显式的销毁它们,避免内存泄漏. 智能指针是一个可以像指针一样工作的对象,有unique_ptr(独占指针),shared_ptr与weak_ptr等智能指针,定义在<memory>头文件中,可以

  • C++智能指针之shared_ptr详解

    目录 共享指针的初始化方式 常用成员函数 shared_ptr内存模型 make_shared的优缺点 优点 缺点 引用计数 比较运算符 总结 共享指针的初始化方式 1.裸指针直接初始化,但不能通过隐式转换来构造 2.允许移动构造,也允许拷贝构造 3.通过make_shared构造 例: #include <iostream> #include <memory> class Frame {}; int main() { std::shared_ptr<Frame> f(

  • C++智能指针shared_ptr

    目录 1.什么是shared_ptr? 2.shared_ptr支持哪些操作? 3.如何创建shared_ptr的实例? 4.什么是shared_ptr的引用计数?如何查看? 5.shared_ptr何时释放其所指向的对象? 1.什么是shared_ptr? C++11中包括shared_ptr在内的多种指针,都是模板类型,意味着使用者可以指定想要操作的类型. 创建shared_ptr的方式如下: shared_ptr<int>p1; // p1=NULL 2.shared_ptr支持哪些操作

  • C++超详细讲解智能指针

    目录 一.内存泄漏-永恒的话题 二.深度思考 三.智能指针分析 四.小结 一.内存泄漏-永恒的话题 动态申请堆空间,用完后不归还 C++ 语言中没有垃圾回收的机制 指针无法控制所指堆空间的生命周期 下面看一段内存泄漏的代码: #include <iostream> #include <string> using namespace std; class Test { int i; public: Test(int i) { this->i = i; } int value()

  • c++智能指针的超详细讲解

    目录 1.什么是智能指针 2.原始指针的问题 3.unique_ptr 4.shared_ptr 5.shared_ptr使用需要注意的点 5.1 不能将一个原始指针初始化多个shared_ptr 5.2.循环引用问题 6.智能指针小结 总结 1.什么是智能指针 从比较简单的层面来看,智能指针是RAII(Resource Acquisition Is Initialization,资源获取即初始化)机制对普通指针进行的一层封装.这样使得智能指针的行为动作像一个指针,本质上却是一个对象,这样可以方

  • C++的智能指针你真的了解吗

    目录 什么是RAII RAII的原理 裸指针存在的问题 auto_ptr unique_ptr 总结 什么是RAII RAII(Resource Acquisition Is Initialization)是由C++之父提出的,中文翻译为资源获取即初始化,使用局部对象来管理资源的技术称为资源获取即初始化:这里的资源主要是指操作系统中有限的东西如内存(heap).网络套接字.互斥量.文件句柄等等,局部对象是指存储在栈的对象,它的生命周期是由操作系统来管理的,无需人工介入 RAII的原理 资源的使用

  • C++智能指针详解

    目录 一. unique_ptr独占指针 特点 创建方式 传递方式 简单使用 隐藏危险 二. shared_ptr 计数指针 特点 传递方式 隐藏危险 三. weak_ptr 优缺点: 智能指针由原始指针的封装,优点是可以自动分配内存,不用担心内存泄漏问题. 用于解决独占/共享所有权指针的释放,传输等问题. 但是没有原始指针方便. 一. unique_ptr独占指针 特点 都是围绕独占展开 特点一: 如其名,独占.也就是说同一个内存空间同时只能有一个指针来管理. int* pi = new in

  • C++ boost scoped_ptr智能指针详解

    目录 一.智能指针-唯一所有者 二.接口类分析 一.智能指针-唯一所有者 boost::scoped_ptr 是一个智能指针,它是动态分配对象的唯一所有者. boost::scoped_ptr 无法复制或移动.此智能指针在头文件 boost/scoped_ptr.hpp 中定义. 二.接口类分析 scoped_array 分析 scoped_array 的类部分原始代码如下: template<class T> class scoped_array // noncopyable { priva

  • C++ Boost PointerContainer智能指针详解

    目录 一.提要 二.智能指针Boost.PointerContainer 三.练习 一.提要 在 C++11 中,Boost.PointerContainer是另一个智能指针,一般是用来生成集合数据的,本文阐述这种指针的特点和用法. 二.智能指针Boost.PointerContainer 库 Boost.PointerContainer 提供专门用于管理动态分配对象的容器.例如,在 C++11 中,您可以使用 std::vector<std::unique_ptr<int>> 创

  • C++学习之移动语义与智能指针详解

    移动语义 1.几个基本概念的理解 (1)可以取地址的是左值,不能取地址的就是右值,右值可能存在寄存器,也可能存在于栈上(短暂存在栈)上 (2)右值包括:临时对象.匿名对象.字面值常量 (3)const 左值引用可以绑定到左值与右值上面,称为万能引用.正因如此,也就无法区分传进来的参数是左值还是右值. const int &ref = a;//const左值引用可以绑定到左值 const int &ref1 = 10;//const左值引用可以绑定到右值 (4)右值引用:只能绑定到右值不能绑

  • 一篇文章带你了解C++智能指针详解

    目录 为什么要有智能指针? 智能指针的使用及原理 RALL shared_ptr的使用注意事项 创建 多个 shared_ptr 不能拥有同一个对象 shared_ptr 的销毁 shared_ptr 的线程安全问题 shared_ptr 的循环引用 unique_ptr的使用 unique_ptr 总结 为什么要有智能指针? 因为普通的指针存在以下几个问题: 资源泄露 野指针 未初始化 多个指针指向同一块内存,某个指针将内存释放,别的指针不知道 异常安全问题 如果在 malloc和free 或

  • C++Smart Pointer 智能指针详解

    目录 一.为啥使用智能指针呢 二.shared_ptr智能指针 三.unique_ptr智能指针 四.weak_ptr智能指针 五.智能指针怎么解决交叉引用,造成的内存泄漏 5.1交叉引用的栗子: 5.2解决方案 六.智能指针的注意事项 总结 一.为啥使用智能指针呢 标准库中的智能指针: std::auto_ptr --single ownership (C++98中出现,缺陷较多,被摒弃) std::unique_ptr --single ownership (C++11替代std::auto

  • C语言指针详解及用法示例

    新手在C语言的学习过程中遇到的最头疼的知识点应该就是指针了,指针在C语言中有非常大的用处.下面我就带着问题来写下我对于指针的一些理解. 指针是什么? 指针本身是一个变量,它存储的是数据在内存中的地址而不是数据本身的值.它的定义如下: int a=10,*p; p=&a int a=10; int *p=&a; 首先我们可以理解 int* 这个是要定义一个指针p,然后因为这个指针存储的是地址所以要对a取地址(&)将值赋给指针p,也就是说这个指针p指向a. 很多新手都会对这两种定义方法

  • C++中函数指针详解及代码分享

    函数指针 函数存放在内存的代码区域内,它们同样有地址.如果我们有一个int test(int a)的函数,那么,它的地址就是函数的名字,如同数组的名字就是数组的起始地址. 1.函数指针的定义方式:data_types (*func_pointer)( data_types arg1, data_types arg2, ...,data_types argn); c语言函数指针的定义形式:返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,-); c++函数指针的定义形式:返回类型 (类名

  • C++ 中的this指针详解及实例

    C++ this 指针详解 学习 C++ 的指针既简单又有趣.通过指针,可以简化一些 C++ 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的.所以,想要成为一名优秀的 C++ 程序员,学习指针是很有必要的. 正如您所知道的,每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址. this指针是类的一个自动生成.自动隐蔽的私有成员,它存在于类的非静态成员中,指向被调用函数所在的对象. 全局仅有一个this指针,当一个对

  • C/C++静态类和this指针详解及实例代码

     C/C++静态类和this指针详解 1.静态类 C++的静态成员不仅可以通过对象来访问,还可以直接通过类名来访问. class CBook{ public: static double price;//需要通过类外来进行初始化 } int main(void){ CBook book; book.price;//通过对象来访问 CBook::price//通过类名来访问 return 0; } 静态成员变量 对应静态成员有以下几点需要注意: (1)静态数据成员可以是当前类的类型,而其他数据成员

随机推荐