【设计模式】代理模式(Proxy Pattern)

news2024/12/25 0:40:12

代理模式属于结构型模式,当一个对象不处于相同内存空间时、创建开销大时、需要进行安全控制时或需要代理处理一些其他事物时可以使用代理模式。代理模式通过为另一个类提供一个替身类来控制对这个类的对象的访问。


文章目录

  • 代理模式的介绍
    • 代理的分类:
    • 优点
    • 缺点
    • 应用场景
    • 注意事项
  • 静态代理模式的使用
    • 类图
      • 三种角色
    • 静态代理实现方法
      • 第一步,编写抽象类
        • 租房接口类
      • 第二步,编写真实对象类
        • 房东类
      • 第三步,编写代理类
        • 房屋中介代理类
      • 第四步,编写客户程序测试
        • 客户程序
        • 测试结果
  • 动态代理模式的使用
    • 动态代理的介绍
    • 通过JDK的`Proxy`类实现动态代理
      • 实现方法
        • 第一步,创建代理实现类
          • MyProxy
        • 第一步,客户程序
          • Main
          • 测试结果
    • 通过CGLIB实现动态代理
      • 添加 jar 包
      • 实现方法
        • 第一步,创建自定义代理类
          • MyCglibProxy
        • 第二步,编写客户程序测试
          • Main
          • 测试结果
  • 本文参考


代理模式的介绍

代理模式(Proxy Pattern)是最常用的设计模式之一,其根本意义是当客户程序不方便直接访问一个对象时,使用一个代理对象间接的控制访问该对象。例如当我需要调用A类时,不直接实例化A类而是实例化它的代理对象B类(它们二者应当继承于同一个父类),在B类中持有A类的实例化对象,以此控制B类间接调用A类。


代理的分类:

  • 远程代理:对于在不同内存空间运行的远程对象,可以使用一个代理对象来远程代理它;

  • 虚拟代理:创建开销很大的对象时,可以先使用代理对象先代替它(占位符),当真实对象创建后,代理对象再将请求委托给它;

  • 安全代理:通过代理对像限制真实对象的访问权限

  • 智能指引:在访问真实对象时使用代理对象执行一些附加的操作,如访问页面时添加点击数;

  • 缓存代理:在客户程序与真实对象间添加一层缓存,用于加速对真实对象的访问速度,例如数据库的连接就存在缓存。

优点

  • 可以在客户端无感知的情况下,对访问对象增加缓存、权限限制等额外功能

  • 具有高扩展性,可以在不修改客户程序和被代理对象的前提下,增加新的代理类

缺点

  • 真实对象与客户程序间多了一层代理对象,可能导致请求处理速度变慢

  • 增加了系统的复杂性

应用场景

  • 在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、事务、日志
  • Spring AOP 底层的实现原理就是基于动态代理
  • 接口请求的缓存功能,对于某些接口请求,如果入参相同,在设定的过期时间内,直接返回缓存结果,而不用重新进行逻辑处理

注意事项

  • 和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制



静态代理模式的使用

举例:租客需要租房,但觉得太麻烦,于是就通过房租中介来租房。

类图

image-20221207204845742

三种角色

  • 抽象主题角色(Subject):声明真实主题角色与代理角色的共同接口方法
  • 真实主题角色(Real Subject):负责执行具体的任务,客户程序可以通过代理角色间接的调用真实主题角色的方法
  • 代理角色(Proxy):持有对真实主题角色的引用,负责调用真实主题角色中相应的接口方法



静态代理实现方法

第一步,编写抽象类

租房接口类

package 设计模式.结构型模式.代理模式.静态代理;

/**
 * **抽象主题角色(Subject)**:声明真实主题角色与代理角色的共同接口方法
 */
public interface 租房接口类 {
    void();
}

第二步,编写真实对象类

房东类

package 设计模式.结构型模式.代理模式.静态代理;

/**
 * **真实主题角色(Real Subject)**:负责执行具体的任务,客户程序可以通过代理角色间接的调用真实主题角色的方法
 */
public class 房东类 implements 租房接口类 {
    @Override
    public void() {
        System.out.println("实现租房");
    }
}

第三步,编写代理类

房屋中介代理类

package 设计模式.结构型模式.代理模式.静态代理;

/**
 * **代理角色(Proxy)**:持有对真实主题角色的引用,负责调用真实主题角色中相应的接口方法
 */
public class 房屋中介代理类 implements 租房接口类 {

    租房接口类 房东; // 持有对真实对象的引用

    public 房屋中介代理类() {
        房东 = new 房东类(); // 创建真实对象
    }

    @Override
    public void() {
        System.out.println("中介找房!");
        房东.();
    }
}

第四步,编写客户程序测试

客户程序

package 设计模式.结构型模式.代理模式;

import 设计模式.结构型模式.代理模式.静态代理.房东类;
import 设计模式.结构型模式.代理模式.静态代理.房屋中介代理类;
import 设计模式.结构型模式.代理模式.静态代理.租房接口类;

public class 客户程序 {

    public static void main(String[] args) {
        // 使用代理模式
        租房接口类 找房 = new 房屋中介代理类();
        找房.();
        // 不使用代理
        System.out.println("————————————");
        租房接口类 房东 = new 房东类();
        房东.();
    }
}

测试结果

中介找房!
实现租房
————————————
实现租房

Process finished with exit code 0

image-20221207205709719




动态代理模式的使用

在真实环境下,静态代理使用得很少,因为真实类的方法很多时,静态代理类的代码量会很大,并且每个真实类都需要一个单独的代理类,所以此时可以使用动态代理

动态代理的介绍

  • 动态代理允许只有一个方法的单个类(代理类)为任意个真实类任意个方法的调用提供服务

  • 动态代理使用Java的反射机制实现,在程序运行期间由JVM动态生成代理类,此时代理类和真实类不用实现同一个接口

  • 但使用反射难免会降低执行效率

  • 动态代理的实现方法有两种

    • 通过JDK的Proxy类实现动态代理
    • 通过CGLIB实现动态代理

通过JDK的Proxy类实现动态代理

此方法实际上代理的是接口,所以要求代理的类必需实现一个接口,并且因为是实现接口,代理对象就只能使用接口的方法。下面我将对HashMapArrayList进行代理,以此控制添加元素时合法性。

实现方法


第一步,创建代理实现类

MyProxy
package 设计模式.结构型模式.代理模式.动态代理.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * JDK的Proxy方式代理
 * 自定义的代理类,其中必须维护一个真实对象,并且实现InvocationHandler接口
 * 此代理对象其实是代理接口,通过动态实现代理接口来实现对真实对象的代理
 */
public class MyProxy implements InvocationHandler {

    private Object bean;

    public MyProxy(Object bean) {
        // 维护一个真实对象
        this.bean = bean;
    }

    /**
     * InvocationHandler的实现方法,代理的核心方法,所有代理对象的请求从这个方法流转到真实对象
     * @param proxy 当前的代理对象
     * @param method 当前需要执行的方法
     * @param args 方法参数列表
     * @return 对接真实对象中方法的返回类型
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 同时代理Map的put方法和List的add方法
        if (method.getName().equals("put") || method.getName().equals("add")){
            String value = null;
            // 如果方法名为put
            if (method.getName().equals("put")){
                System.out.println("map存入的键:"+args[0]+"\t 值:"+args[1]);
                value = (String) args[1];
            }else { // 不为put肯定为add
                System.out.println("list存入的值:"+args[0]);
                value = (String) args[0];
            }
            // List的add方法的返回值是布尔型,所以返回false而不能返回null,而Map的put方法返回值不明确,所以仅考虑add方法
            if (!filter(value)) return false;
        }
        // 调用真实对象的指定方法并返回值 bean:指定真实对象(千万记得是真实对象,填proxy会死循环) args:传参列表
        return method.invoke(bean, args);
    }

    /**
     * 过滤掉黄色信息
     * @param value 原信息
     * @return false 不能通行信息 true 可通行信息
     */
    boolean filter(String value){
        if (value.equals("黄色")){
            System.out.println("好啊!搞黄色,不能存不能存!");
            return false;
        }else{
            System.out.println("很好,没有搞黄色,允许存入");
        }
        return true;
    }
}

第一步,客户程序

Main
package 设计模式.结构型模式.代理模式.动态代理.jdk;

import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Main {

    /**
     * static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 新建一个代理对象
     * loader : 类加载器,一般默认用代理类的类加载器,为null也可以
     * inerfaces: 代理对象需要实现的接口类
     * InvocationHandler: 代理方法的实现类
     *
     * 使用Proxy代理要求代理类必须实现某个接口,Proxy会动态创建实现这个接口的代理类,所以也只能使用接口定义的方法
     * 就和静态代理的结构一样,只不过现在不需要手动创建代理类而是动态生成
     * 此示例中的HashMap实现的Map接口,ArrayList实现的List的接口,他们的代理对象也是实现这两个接口
     */

    public static void main(String[] args) {
        // 创建代理对象,代理对象中必须维护一个真实对象,值都存在真实对象上
        MyProxy myProxy = new MyProxy(new HashMap<String,String>());
        Map<String, String> map = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class}, myProxy);
        System.out.println("————存储Map数据————");
        map.put("张三","这是正经信息");
        map.put("李四","黄色");
        System.out.println("————读取Map数据————");
        System.out.println("张三的信息:"+map.get("张三"));
        System.out.println("李四的信息:"+map.get("李四"));

        System.out.println("\n");
        // 新建代理对象,其中必须维护一个真实对象,值都存在真实对象上
        myProxy = new MyProxy(new ArrayList<String>());
        List<String> list = (List) Proxy.newProxyInstance(myProxy.getClass().getClassLoader(),new Class[]{List.class},myProxy);
        System.out.println("————存储List数据————");
        list.add("这是正经信息");
        list.add("黄色");
        System.out.println("————读取List数据————");
        list.forEach(System.out::println);
    }
}
测试结果
————存储Map数据————
map存入的键:张三	 值:这是正经信息
很好,没有搞黄色,允许存入
map存入的键:李四	 值:黄色
好啊!搞黄色,不能存不能存!
————读取Map数据————
张三的信息:这是正经信息
李四的信息:null


————存储List数据————
list存入的值:这是正经信息
很好,没有搞黄色,允许存入
list存入的值:黄色
好啊!搞黄色,不能存不能存!
————读取List数据————
这是正经信息

Process finished with exit code 0

img-V3hoIjy2-1670513095568



通过CGLIB实现动态代理

CGLIB(Code Generation Library)是一个高性能开源的代码生成包,它被许多框架所使用,其底层是通过使用一个小而快的字节码处理框架 ASM(Java 字节码操控框架)转换字节码并生成新的类。因此 CGLIB 要依赖于 ASM 的包。

JDK 的动态代理机制只能代理实现了接口的类,而对于没有实现接口的类就不能使用JDK 的 Proxy 类生成代理对象,cglib 是针对类来实现代理的,他的原理是对指定的目标类生成一个子类并通过回调的方式来实现增强,但因为采用的是继承,所以不能对 final 修饰的类进行代理

添加 jar 包

  • cglib.jarasm.jar

  • 如果使用cglib-nodep.jar则不需要添加asm.jar,因为包内部包含asm的类

  • CGLIB的下载(github)

实现方法


第一步,创建自定义代理类

MyCglibProxy
package 设计模式.结构型模式.代理模式.动态代理.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * CGLIB动态代理
 * 通过动态创建一个被代理对象的子类来实现代理,此方法并不需要被代理类实现接口,所以可以用于普通对象的代理
 */
public class MyCglibProxy implements MethodInterceptor {

    private Enhancer enhancer; // 增强类,代理类
    private Object bean; // 真实对象,其实通过invokeSuper方法调用父类方法就行,没必要维护一个真实对象

    public MyCglibProxy(Object bean) {
        // 维护一个真实对象
        this.bean = bean;
        // 代理对象
        enhancer = new Enhancer();
    }

    /**
     * 获取代理对象
     * @return
     */
    public Object getProxy(){
        // 设置父类,也就是要代理的类
        enhancer.setSuperclass(bean.getClass());
        // 设置回调方法,也就是代理类需要执行的方法
        enhancer.setCallback(this);
        // 创建一个代理对象
        return enhancer.create();
    }

    /**
     * 代理转发,所有代理对象的请求从这个方法流转到真实对象
     * @param o 当前代理对象
     * @param method 当前方法
     * @param objects 方法参数列表
     * @param methodProxy 方法代理,通过方法代理调用方法可以提供执行速度
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 同时代理Map的put方法和List的add方法
        if (method.getName().equals("put") || method.getName().equals("add")){
            String value = null;
            if (method.getName().equals("put")){
                System.out.println("map存入的键:"+objects[0]+"\t 值:"+objects[1]);
                value = (String) objects[1];
            }else {
                System.out.println("list存入的值:"+objects[0]);
                value = (String) objects[0];
            }
            // List的add方法的返回值是布尔型,所以返回false而不能返回null,而Map的put方法返回值不明确,所以仅考虑add方法
            if (!filter(value)) return false;
        }
        // jdk的代理调用方式,调用速度比后两者慢,不推荐
        //method.invoke(bean,objects);
        // 调用真实对象的指定方法,但使用FastClass提高了调用效率
        //methodProxy.invoke(bean,objects);
        // 调用当前代理对象的父类(也就是被代理对象)的方法,这样做可以不用维护真实对象(这里不能填真实对象,否则会报异常)
        //methodProxy.invokeSuper(o,objects);
        return methodProxy.invokeSuper(o, objects);
    }


    /**
     * 过滤掉黄色信息
     * @param value 原信息
     * @return false 不能通行信息 true 可通行信息
     */
    boolean filter(String value){
        if (value.equals("黄色")){
            System.out.println("好啊!搞黄色,不能存不能存!");
            return false;
        }else{
            System.out.println("很好,没有搞黄色,允许存入");
        }
        return true;
    }

}

第二步,编写客户程序测试

Main
package 设计模式.结构型模式.代理模式.动态代理.cglib;

import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        // 创建自定义代理对象
        MyCglibProxy myCglibProxy = new MyCglibProxy(new HashMap<String,String>());
        // 获取代理对象
        HashMap<String, String> map = (HashMap) myCglibProxy.getProxy();
        System.out.println("————存储Map数据————");
        map.put("张三","这是正经信息");
        map.put("李四","黄色");
        System.out.println("————读取Map数据————");
        System.out.println("张三的信息:"+map.get("张三"));
        System.out.println("李四的信息:"+map.get("李四"));

        System.out.println("\n");
        // 新建自定义代理对象
        myCglibProxy = new MyCglibProxy(new ArrayList<String>());
        // 获取代理对象
        ArrayList<String> list = (ArrayList) myCglibProxy.getProxy();
        System.out.println("————存储List数据————");
        list.add("这是正经信息");
        list.add("黄色");
        System.out.println("————读取List数据————");
        list.forEach(System.out::println);
    }
}
测试结果

同JDK方法一样

————存储Map数据————
map存入的键:张三	 值:这是正经信息
很好,没有搞黄色,允许存入
map存入的键:李四	 值:黄色
好啊!搞黄色,不能存不能存!
————读取Map数据————
张三的信息:这是正经信息
李四的信息:null


————存储List数据————
list存入的值:这是正经信息
很好,没有搞黄色,允许存入
list存入的值:黄色
好啊!搞黄色,不能存不能存!
————读取List数据————
这是正经信息

Process finished with exit code 0



本文参考

一文搞懂代理模式-CSDN

代理模式详细讲解-CSDN

代理模式是个什么模式?-知乎

CGLIB动态代理之intercept函数刨析-CSDN

浅谈Java代理:JDK动态代理-Proxy.newProxyInstance-CSDN

【设计模式】代理模式-原理、实现以及应用场景-CSDN

代理模式-菜鸟教程

代理模式——远程代理(一)-CSDN

CGLIB(Code Generation Library) 介绍与原理

Java中类加载器-CSDN

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/73932.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

了解CloudCompare软件

CloudCompare是一款基于GPL开源协议的3D点云处理软件&#xff0c; 打开一个点云&#xff1b; 按住鼠标拖动旋转会出现坐标轴&#xff1b; 如果选中 TLS/GBL&#xff0c; 会出来右边这个线框&#xff0c;可以一起旋转&#xff1b;查了一下&#xff0c;TLS/GBL似乎是地面激光雷达…

2022年国际工程行业研究报告

第一章 行业概况 国际工程是指一个工程项目从咨询、投资、招投标、承包(包括分包)、设备采购、培训到监理各个阶段的参与者来自不止一个国家&#xff0c;并且按照国际工程项目管理模式进行管理的工程。国际工程是一种综合性的国际经济合作方式&#xff0c;是国际技术贸易的一种…

了解Linux内核内存映射

【推荐阅读】 深入linux内核架构--进程&线程 路由选择协议——RIP协议 轻松学会linux下查看内存频率,内核函数,cpu频率 纯干货&#xff0c;linux内存管理——内存管理架构&#xff08;建议收藏&#xff09; 概述Linux内核驱动之GPIO子系统API接口 一. 内存映射原理 由于所…

shadow阴影属性

shadow阴影属性 盒子阴影CSS中新增了盒子阴影&#xff0c;我们可以使用box-shadow属性为盒子添加阴影 源代码 box-shadow: h-shadow v-shadow blur spread color inset; h-shadow 必需&#xff0c;水平阴影的位置&#xff0c;允许负值 v-shadow…

AI 揭晓答案,2022世界杯冠军已出炉

卡塔尔世界杯&#xff0c;究竟谁能捧起大力神杯&#xff0c;就让我们用机器学习预测一下吧&#xff01; 文章目录数据源技术提升数据集构建功能开发数据分析模型世界杯模拟结论数据源 为了构建机器学习模型&#xff0c;我们需要来自团队的数据。首先需要一些能够说明球队表现的…

Java学习之多态参数

目录 多态参数 父类-Employee类 子类-Worker类 子类-Manager类 Test类-要求1 main类-PolyParameter 在main类中调用Test类的showEmpAnnual(Employee e) 方法 运行结果 Test类-要求2 代码 main方法内调用 分析 运行结果 多态参数 方法定义的形参类型是父类&#xff0…

青竹画材创业板IPO被终止:年营收4.15亿 文投基金是股东

雷递网 雷建平 12月8日河北青竹画材科技股份有限公司&#xff08;简称&#xff1a;“青竹画材”&#xff09;日前IPO被终止。青竹画材曾计划募资4.1亿元&#xff0c;其中&#xff0c;3.08亿元用于美术画材产能扩建项目&#xff0c;2317.35万元用于研发中心项目&#xff0c;7966…

Selenium+python常见问题,闪退,找不到元素

1、闪退问题&#xff1a; 由于缺少浏览器对应驱动。谷歌&#xff1a;chromedriver驱动&#xff1b;火狐&#xff1a;geckodriver驱动 查看Chrome版本 查找Chrome和ChromeDriver的对应关系 打开Chrome&#xff0c;在设置 – 关于Chrome下载对应ChromeDriver ChromeDriver下载…

本地存储:localStorage,sessionStorage,和cookie。区别

localStorage&#xff1a; 特点&#xff1a; 永久存储支持跨页面通讯&#xff0c;也就是在其他页面同样可以获取到你存好的数据。只能存储字符串类型的数据&#xff0c;不能存储复杂数据类型 sessionStorage&#xff1a; 特点&#xff1a; 临时存储&#xff0c;会话级别&am…

68. 关于 SAP ABAP 报表的多语言显示问题

文章目录 SAP 标准程序的多语言显示支持自开发 Screen 的字段,如何实现多语言显示呢?总结最近一个朋友向我发起知乎咨询,询问 SAP ABAP 报表的字段文本,到底是存储在哪些数据库表里的。 随着后来笔者同这位朋友的深入沟通,得知这位朋友询问的问题背后,实际上包含了一个重…

Windows系统还原失败该怎么办?

系统还原是Windows系统中一个强大的实用程序&#xff0c;它允许您将系统回退到以前的时间点及状态。该功能可以用来撤销对系统所做的一系列更改&#xff0c;比如&#xff0c;当您执行了驱动程序的更新后&#xff0c;计算机出现了异常&#xff0c;您就可以通过系统还原的操作将系…

【微服务】1、一篇文章了解 Eureka 注册中心(理论加代码)

Eureka 的作用和案例使用一、Eureka 的作用&#xff08;注册中心&#xff09;二、Eureka 代码(1) 搭建 Eureka 注册中心① 创建项目&#xff0c;引入 spring-cloud-starter-netflix-eureka-server 依赖【eureka 服务端依赖】② 编写 eureka 服务端启动类&#xff08;启动类添加…

WSO2使用Jenkins进行CI/CD

WSO2使用Jenkins进行CI/CD1. Tool Install1.0 Port 1.1 wso2 Install 1.2 Tool Install(git/vim/jenkins)2. Jenkins CI/CD2.1 Apictl Command2.1 Apictl Command5.2.1 Setting up the environment5.2.2 Setup repository5.2.3 Setup JFrog Artifactory5.2.4 Configure Jenkins…

计算机网络复习(四)

4-15.一个3200位长的TCP报文传到IP层&#xff0c;加上160位的首部后成为数据报。下面的互联网由两个局域网通过路由器连接起来。但第二个局域网所能传送的最长数据帧中的数据部分只有1200位。因此数据报在路由器必须进行分片。试问第二个局域网向其上层要传送多少比特的数据&am…

JavaScript进阶教程——原始类型与引用类型、对象拷贝(克隆)

文章目录序原始类型与引用类型基本概念两种类型的区别赋值比较函数传参对象拷贝对象克隆的使用场景&#xff1a;对象拷贝的分类&#xff1a;浅拷贝深拷贝序 解决JavaScript中比较烧脑的问题&#xff1a; 原始类型与引用类型闭包原型对象this关键字bind、apply、call异步编程 …

VMware Workstation 17.0 Pro SLIC Unlocker for Windows

VMware_Dell_2.6_BIOS-EFI64_Mod&#xff1b;macOS Unlocker&#xff0c;支持 macOS Ventura 请访问原文链接&#xff1a;VMware Workstation 17.0 Pro SLIC & Unlocker for Windows & Linux&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&…

Win11安装Linux子系统提示错误代码0x800701bc怎么办?

Win11安装Linux子系统提示错误代码0x800701bc怎么办&#xff1f;最近有用户想要在自己的电脑上安装一个Linux子系统来使用&#xff0c;但是在安装的过程中却出现了一些问题&#xff0c;提示错误代码0x800701bc。那么这个情况要怎么去处理&#xff0c;一起来看看解决的操作步骤吧…

GitHub 被超火的 ChatGPT 霸榜!

本期推荐开源项目目录&#xff1a;1. ChatGPT2. 基于 Node.js 的 ChatGPT3. Mac 版的 ChatGPT4. Chrome 插件的 ChatGPT最近 ChatGPT 火爆全球&#xff0c;估计各位的朋友圈都被这东西刷屏了吧。Chat GPT 是 OpenAI 推出的基于 GPT-3 技术的聊天机器人。它能自动理解用户发的消…

小度机器人3D数字勋章盲盒发售!抽盲盒,赢奖金!参与合成得稀有!

人气王小度机器人3D数字勋章盲盒来了&#xff01;玩法升级&#xff0c;福利更多&#xff01;参与活动&#xff0c;抽超限量稀有款&#xff0c;赢京东卡福利&#xff0c;更能合成珍藏款勋章&#xff01;【活动介绍】小度机器人是百度公司推出的智能交互机器人&#xff0c;依托百…

C++语法——详解智能指针的概念、实现原理、缺陷

目录 一.智能指针的概念 &#xff08;一&#xff09;.智能指针的历史 &#xff08;二&#xff09;.智能指针的使用 插曲.auto_ptr ①unique_ptr ②shared_ptr ③weak_ptr 二.智能指针的实现 三.智能指针的缺陷 &#xff08;一&#xff09;.循环引用 &#xff08;二&…