首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

深度学习框架 PyTorch 发布 1.11 版本

Python 著名 AI 框架 PyTorch 1.11 已发布。此版本由自 1.10 以来的 3300 多个提交组成, TorchData 和 functorch 的 beta 版本亦伴随发布。 版本概括: TorchData 是一个用于通用模块化数据加载原语的新库,用于轻松构建灵活和高性能的数据管道。在 GitHub 上查看。 functorch 是一个将可组合函数转换添加到 PyTorch 的库,现已推出 beta 版。在 GitHub 上查看。 分布式数据并行 (DDP) 静态图优化稳定可用。 TorchData 介绍 TorchData 是一个通用模块化数据加载原语库,用于轻松构建灵活且高性能的数据管道。现有的 DataLoader 将太多的功能捆绑在一起,难以扩展,此外,不同的用例通常必须一遍又一遍地重写相同的数据加载实用程序。TorchData 的目标是通过称为“ DataPipes ”的 Iterable 样式和 Map 样式的构建块启用可组合的数据加载,这些构建块与 PyTorch 的 DataLoader 开箱即用。 目前已经实现了 50 多个提供不同核心功能的 DataPipes,例如打开文件、解析文本、转换样本、缓存、混洗和批处理。在此版本中,一些 PyTorch 域库已迁移其数据集以使用 DataPipes。在 TorchText 中,库提供的流行数据集是使用 DataPipes 实现的,其 SST-2 二进制文本分类教程的一部分演示了如何使用 DataPipes 为模型预处理数据。 TorchData 的文档现已上线,包含介绍如何使用 DataPipes、将它们与 DataLoader 一起使用以及实现自定义。项目的 README 文件中描述了与 DataLoader 相关的常见问题解答和未来计划。 functorch 介绍 受Google JAX 的极大启发,functorch 是一个向 PyTorch 添加可组合函数转换的库。它旨在提供可组合的 vmap(矢量化)和 autodiff 转换,可与 PyTorch 模块和 PyTorch autograd 一起使用,并具有良好的渴望模式性能。 可组合的函数转换可以帮助解决当今在 PyTorch 中难以实现的许多用例: 计算每样本梯度(或其他每样本数量) 在单台机器上运行模型集合 在 MAML 的内循环中有效地将任务批处理在一起 有效地计算雅可比矩阵和黑森矩阵以及批量矩阵 组合 vmap(矢量化)、vjp(反向模式 AD)和 jvp(正向模式 AD)转换能够轻松地表达上述内容,而无需为每个转换设计单独的库。有关 functorch 的详细信息,请参阅文档、教程和安装说明。 分布式训练 (稳定)DDP 静态图 DDP 静态图假设您的模型在每次迭代中都使用相同的一组已使用/未使用的参数,因此它可以确定性地了解状态,例如哪些 hooks 将触发、hooks 将触发多少次以及第一次迭代后的梯度计算就绪顺序。 静态图在第一次迭代中缓存了这些状态,因此它可以支持 DDP 在以前的版本中无法支持的功能,例如,支持在相同参数上的多个激活检查点,无论是否有未使用的参数。当存在未使用的参数时,静态图功能还应用性能优化,例如,它避免遍历图以在每次迭代中搜索未使用的参数,并启用动态分桶顺序。 DDP 静态图中的这些优化为一些推荐模型带来了 10% 的 QPS 增益。 要启用静态图,只需在 DDP API 中设置 static_graph=True ,如下所示: ddp_model = DistributedDataParallel(model, static_graph=True) 有关更多详细信息,请参阅文档和教程。 更新公告:https://pytorch.org/blog/pytorch-1.11-released/

优秀的个人博客,低调大师

mysql学习-条件查询和范围查询

# 条件查询 ## WHERE子句 该语句可以实现按照一定的条件进行查询功能 语法 ```sql SELECT FROM WHERE ``` 以下是常见的比较运算符 |运算符|说明| |:-: | :-:| |=|等于| |>|大于| |<|小于| |>=|大于等于| |<=|小于等于| |!>|不大于| |!<|不小于| |<>或!=|等于| ## 使用比较与运算符限制查询结果 1. 使用=查询数据 如果相同条件满足,将该记录显示 ```sql SELECT * FROM goods WHERE id = 22; ``` 2. 使用>查询数据 大于所指定条件的数据 ```sql SELECT id, `name`, views_count FROM goods WHERE views_count > 0 ``` 3. 使用 =查询 ```sql SELECT id, `name`, views_count FROM goods WHERE views_count >= 16 ``` 5. <=查询 ```sql SELECT id, `name`, views_count FROM goods WHERE views_count <= 16 ``` 6. !=查询 ```sql SELECT id, `name`, original_price FROM goods WHERE original_price != 2000 ``` # 范围查询 范围查询是用来查询给定的两个值之间的数据,通常使用BETWEEN …… AND 和 NOT …… BETWEEN …… AND来指定范围条件 在使用BETWEEN …… AND查询条件的时候,指定的第一个值必须小于第二个值,因为BETWEEN …… AND的实质是查询条件大于或者等于第一个值,并且小于等于第二个值的简写形式,等价于比较运算符">=....<=" 1. 查询两个值之间的数据 ```sql SELECT id AS '商品id', `name` AS '商品名称', original_price AS '价格' FROM goods WHERE original_price BETWEEN 1000 AND 2000; ``` 2. 查询两个日期之间的数据 查询两个日期之间的数据也可以使用BETWEEN来实现,可以使用日期类型的数据作为查询条件 ```sql SELECT id, `name`, addtime FROM goods WHERE addtime BETWEEN '2016-07-20 09:58:53' AND '2016-07-20 10:58:53'; ``` 3. 使用日期函数 通过GETDATE()函数和DATEADD()函数,获取当天的日期和前一天的日期,在通过使用BETWEEN……AND来查询在这两个日期之间的数据 ```sql SELECT GETDATE(); ``` 4. 查询不在两个数之间的数据 可以使用NOT BETWEEN…… AND 来实现 ```sql SELECT id, `name`, original_price FROM goods WHERE original_price NOT BETWEEN 2000 AND 3000; ``` 5. 日期时间查询 5.1 转换日期格式 有时候数据库中表存储的日期可能不是规范的日期形式,为了方便用户查看,需要将日期转换为四位数字的年份,同时改变日期格式,如将‘02.09.02’转换为‘2002-09-02’的格式 - 1. 长日期格式转换为短日期的格式 CONVERT(char(10),getdat(),120) 将日期转换成yyyy-mm-dd格式时间,其中120是格式代码,char(10)是指取出前10位字符 - 2. 将日期中的‘-’转换为'/' REPLACE()函数寻找列值中的'-',将其转换为'/' ```sql -- 查询所有图书信息 SELECT id ,`name`,current_price,addtime FROM goods ORDER BY addtime; -- 把长日期格式数据转换为短日期格式数据 SELECT id,`name`,current_price,CONVERT(char(10),addtime,120) AS '录入日期' FROM goods ORDER BY id; -- 将日期的格式中的'-'转换为'/' SELECT id,`name`,`REPLACE`(CONVERT(char(10),addtime,120,'-','/') AS '录入日期' FROM goods ORDER BY id; ``` 5.2 计算两个日期的时间间隔天数 使用DATEDIFF()函数计算日期间隔天数 语法(MYSQL) DATEDIFF(expr1,expr2) 参数说明: 该函数返回其实时间expr1和结束时间expr2之间的天数,expr1和expr2为日期或者date-and-time表达式,计算中只取这些值的日期部分 ```sql SELECT 书号,书名, CONVERT ( CHAR (10), 出版日期,120 ) 出版日期, CONVERT ( CHAR (10), 下一次出版日期,120 ) 出版日期, DATEDIFF( DAY, 出版日期,下一次出版日期 ) 两次出版相差的天数 FROM goods ``` 5.3 按照指定日期查询数据 - 1. DAY()函数 返回代表指定的日期的天日期部分的整数 DAY(date) 参数说明: date: 类型为datetime 或者smalldatetime的表达式 DAY()函数返回值的数据类型为int ```sql SELECT DAY(0) AS MY_DAY1,DAY('02/03/2018') AS MY_DAY2 ``` - 2. MONTH()函数 实现按月查询 ```sql SELECT "month number" = MONTH('03.11.2017') ``` - 3. YEAR()函数 返回表示指定日期中的年份的整数 ```sql SELECT YEAR(-1) AS MY_YEAR1, YEAR(3) AS MY_YEAR2, YEAR('08/13/2018') AS MY_YEAR3; ``` 举例 查询日期的月和年 ```sql SELECT id, `name`, is_sale, addtime FROM goods WHERE MONTH (addtime) = 7 AND YEAR (addtime) = 2016 AND is_sale = 1; ```

优秀的个人博客,低调大师

HarmonyOS学习路之开发篇——Intent

Intent 基本概念 Intent是对象之间传递信息的载体。例如,当一个Ability需要启动另一个Ability时,或者一个AbilitySlice需要导航到另一个AbilitySlice时,可以通过Intent指定启动的目标同时携带相关数据。Intent的构成元素包括Operation与Parameters,具体描述参见表1。 表1 Intent的构成元素 当Intent用于发起请求时,根据指定元素的不同,分为两种类型: 如果同时指定了BundleName与AbilityName,则根据Ability的全称(例如“com.demoapp.FooAbility”)来直接启动应用。 如果未同时指定BundleName和AbilityName,则根据Operation中的其他属性来启动应用。 说明 Intent设置属性时,必须先使用Operation来设置属性。如果需要新增或修改属性,必须在设置Operation后再执行操作。 关于Intent最简单的使用方法,可参见快速入门的示例代码。其中“实现页面跳转”重点描述了使用Intent实现两个页面跳转关系的操作。 根据Ability的全称启动应用 通过构造包含BundleName与AbilityName的Operation对象,可以启动一个Ability、并导航到该Ability。示例代码如下: Intent intent = new Intent(); // 通过Intent中的OperationBuilder类构造operation对象,指定设备标识(空串表示当前设备)、应用包名、Ability名称 Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName("com.demoapp") .withAbilityName("com.demoapp.FooAbility") .build(); // 把operation设置到intent中 intent.setOperation(operation); startAbility(intent); 作为处理请求的对象,会在相应的回调方法中接收请求方传递的Intent对象。以导航到另一个Ability为例,导航的目标Ability可以在其onStart()回调的参数中获得Intent对象。 根据Operation的其他属性启动应用 有些场景下,开发者需要在应用中使用其他应用提供的某种能力,而不感知提供该能力的具体是哪一个应用。例如开发者需要通过浏览器打开一个链接,而不关心用户最终选择哪一个浏览器应用,则可以通过Operation的其他属性(除BundleName与AbilityName之外的属性)描述需要的能力。如果设备上存在多个应用提供同种能力,系统则弹出候选列表,由用户选择由哪个应用处理请求。以下示例展示使用Intent跨Ability查询天气信息。 请求方 在Ability中构造Intent以及包含Action的Operation对象,并调用startAbilityForResult()方法发起请求。然后重写onAbilityResult()回调方法,对请求结果进行处理。 private void queryWeather() { Intent intent = new Intent(); Operation operation = new Intent.OperationBuilder() .withAction(Intent.ACTION_QUERY_WEATHER) .build(); intent.setOperation(operation); startAbilityForResult(intent, REQ_CODE_QUERY_WEATHER); } @Override protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) { switch (requestCode) { case REQ_CODE_QUERY_WEATHER: // Do something with result. ... return; default: ... } } 处理方 1、作为处理请求的对象,首先需要在配置文件中声明对外提供的能力,以便系统据此找到自身并作为候选的请求处理者。 { "module": { ... "abilities": [ { ... "skills":[ { "actions":[ "ability.intent.QUERY_WEATHER" ] } ] ... } ] ... } ... } 2、在Ability中配置路由以便支持以此action导航到对应的AbilitySlice。 @Override protected void onStart(Intent intent) { ... addActionRoute(Intent.ACTION_QUERY_WEATHER, DemoSlice.class.getName()); ... } 3、在Ability中处理请求,并调用setResult()方法暂存返回结果。 @Override protected void onActive() { ... Intent resultIntent = new Intent(); setResult(0, resultIntent); //0为当前Ability销毁后返回的resultCode。 ... }

优秀的个人博客,低调大师

安全技术学习——NAT444实验

文章目录 组网需求 基础配置 接口加入相应的安全区域 配置NAT策略 防火墙配置安全策略 配置默认路由 实验拓扑【GitHub下载】 组网需求 A运营商公网IPv4地址比较匮乏,为尽量节约公网地址,其内部城域网使用的是私网IPv4地址。为满足城域网下的私网用户访问Internet的需求,需要在CPE和CGN设备上两次使用NAT,使IPv4私网用户能够访问IPv4公网。 如下实验拓扑: 实验需求: 完成相关配置,实现客户端可以访问Server端 配置步骤: 如图所示,完成基础的IP地址配置 将CPE、CGN的接口加入相应的区域 在CPE、CGN上配置NAT策略 在CPE、CGN配置安全策略 配置默认路由 基础配置 ---------------------------------------- # CPE基础配置 sys CPE int g1/0/0 ip ad 192.168.0.1 24 int g1/0/1 ip ad 10.1.1.1 24 ---------------------------------------- # CGN基础配置 sys CGN int g1/0/0 ip ad 10.1.2.1 24 int g1/0/1 ip ad 1.1.1.1 24 ---------------------------------------- # AR1基础配置 sysname AR1 # interface GigabitEthernet0/0/0 ip address 10.1.1.2 255.255.255.0 # interface GigabitEthernet0/0/1 ip address 10.1.2.2 255.255.255.0 ---------------------------------------- # AR2基础配置 sysname AR2 # interface GigabitEthernet0/0/0 ip address 1.1.1.2 255.255.255.0 # interface LoopBack0 ip address 2.2.2.2 255.255.255.255 接口加入相应的安全区域 CPE配置 [CPE]firewall zone trust //进入trust区域 [CPE-zone-trust]add interface g1/0/0 //将接口加入区域 [CPE-zone-trust]q [CPE]firewall zone untrust [CPE-zone-untrust]add interface g1/0/1 CGN配置 [CGN]firewall zone trust [CGN-zone-trust]add interface g1/0/0 [CGN-zone-trust]q [CGN]firewall zone untrust [CGN-zone-untrust]add interface g1/0/1 对于CGN而言,PC、CPE、IPv4Network都为信任区域 IPv4Network对于CPE来说为非信任区域,对于CGN来说为信任区域 配置NAT策略 CPE配置NAT策略 使用Easy-IP的配置方式 [CPE]nat-policy [CPE-policy-nat]rule name CPE //rule名字CPE [CPE-policy-nat-rule-CPE]source-zone trust //源区域 [CPE-policy-nat-rule-CPE]destination-zone untrust //目标区域 [CPE-policy-nat-rule-CPE]source-address 192.168.0.0 24 //源IP [CPE-policy-nat-rule-CPE]action source-nat easy-ip //配置Easy-IP CGN配置NAT策略 使用动态地址池的配置方式 配置地址池 [CGN]nat address-group 1 [CGN-address-group-1]mode pat //NAT模式为一对一转换 [CGN-address-group-1]route enable //地址池自动生成 [CGN-address-group-1]section 0 1.1.1.11 1.1.1.20 //创建地址池 配置NAT策略 [CGN]nat-policy [CGN-policy-nat]rule name CGN [CGN-policy-nat-rule-CGN]source-zone trust [CGN-policy-nat-rule-CGN]destination-zone untrust [CGN-policy-nat-rule-CGN]source-address 10.1.1.0 24 [CGN-policy-nat-rule-CGN]action source-nat address-group 1 //配置动态地址池 防火墙配置安全策略 CPE安全策略配置 配置安全策略 # 进入安全策略视图 [CPE]security-policy # 创建安全规则 [CPE-policy-security]rule name tr_to_un # 设置安全规则的源安全地址为trust [CPE-policy-security-rule-tr_to_un]source-zone trust # 设置安全规则的目的安全地址为untrust [CPE-policy-security-rule-tr_to_un]destination-zone untrust # 设置安全规则源网段 [CPE-policy-security-rule-tr_to_un]source-address 192.168.0.0 24 # 设置安全规则的动作为允许 [CPE-policy-security-rule-tr_to_un]action permit 接口放行所有服务,允许ping [CPE-GigabitEthernet1/0/0]service-manage all permit CGN安全策略配置 配置安全策略 [CGN]security-policy [CGN-policy-security]rule name tr_to_un [CGN-policy-security-rule-tr_to_un]source-zone trust [CGN-policy-security-rule-tr_to_un]destination-zone untrust [CGN-policy-security-rule-tr_to_un]source-address 10.1.1.0 24 [CGN-policy-security-rule-tr_to_un]action permit 接口放行所有服务,允许ping [CGN-GigabitEthernet1/0/0]service-manage all permit 配置默认路由 CPE指定默认路由 [CPE]ip route-static 0.0.0.0 0 10.1.1.2 AR1配置默认路由 [AR1]ip route-static 0.0.0.0 0.0.0.0 10.1.2.1 CGN配置默认路由 [CGN]ip route-static 0.0.0.0 0 1.1.1.2 //去往Server的路由 [CGN]ip route-static 10.1.1.1 24 10.1.2.2 //回客户端的路由 AR2配置默认路由 [AR2]ip route-static 0.0.0.0 0 1.1.1.1 注意事项: 因为是模拟的运营商网络,所以必须配置默认路由,否则无法ping通 在防火墙的接口下,必须允许ping,不然无法ping通 AR1(IPv4 Network)需要配置任意路由,否则无法ping通CGN右侧的(untrust)区域 对于CGN而言,所有的数据流量都是从10.1.1.0网段发来的,所以安全策略的源IP直接是10.1.1.0 以上内容均属原创,如有不详或错误,敬请指出。

优秀的个人博客,低调大师

再谈 Send 与 Sync | Rust学习笔记

Send 与 Sync 可能是Rust多线程以及异步代码种最常见到的约束。在前面一篇讨论多线程的文章中介绍过这两个约束的由来。但是,真正书写比较复杂的代码时,还是会经常遇到编译器的各种不配合。这里借用我的同事遇到的一个问题再次举例谈一谈 Send 与 Sync 的故事。 基本场景 C/C++中不存在Send/Sync的概念,数据对象可以任意在多线程中访问,只不过需要程序员保证线程安全,也就是所谓“加锁”。而在Rust中,由于所有权的设计,不能直接将一个对象分成两份或多份,每个线程都放一份。一般地,如果一份数据仅仅子线程使用,我们会将数据的值转移至线程中,这也是Send的基础含义。因此,Rust代码经常会看到将数据clone(),然后move到线程中: let b = aa.clone(); thread::spawn(move || { b... }) 假如,数据需要在多线程共享,情况会复杂一些。我们一般不会在线程中直接使用外部环境变量引用。原因很简单,生命周期的问题。线程的闭包要求‘static,这会与被借用的外部环境变量的生命周期冲突,错误代码如下: let bb = AA::new(8); thread::spawn( || { let cc = &bb; //closure may outlive the current function, but it borrows `bb`, which is owned by the current function }); 包裹一个Arc可以解决这个问题,Arc恰好就是用来管理生命周期的,改进后的代码如下: let b = Arc::new(aa); let b1 = b.clone(); thread::spawn(move || { b1... }) Arc提供了共享不可变引用的功能,也就是说,数据是只读的。如果我们需要访问多线程访问共享数据的可变引用,即读写数据,那么还需要在原始数据上先包裹Mutex<T>,类似于RefCell<T>,提供内部可变性,因此我们可以获取内部数据的&mut,修改数据。当然,这需要通过Mutex::lock() 来操作。 let b = Arc::new(Mutex::new(aa)); let b1 = b.clone(); thread::spawn(move || { let b = b1.lock(); ... }) 为什么不能直接使用RefCell完成这个功能?这是因为RefCell不支持 Sync,没办法装入Arc。注意Arc的约束: unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {} 若 Arc<T>是Send,条件是 T:Send+Sync。RefCell不满足 Sync,因此 Arc<RefCell<>> 不满足Send,无法转移至线程中。错误代码如下: let b = Arc::new(RefCell::new(aa)); let b1 = b.clone(); thread::spawn(move || { ^^^^^^^^^^^^^ `std::cell::RefCell<AA<T>>` cannot be shared between threads safely let x = b1.borrow_mut(); }) 异步代码:跨越 await 问题 如上所述,一般地,我们会将数据的值转移入线程,这样只需要做正确的 Send和Sync 标记即可,很直观,容易理解。典型的代码如下: fn test1<T: Send + Sync + 'static>(t: T) { let b = Arc::new(t); let bb = b.clone(); thread::spawn( move|| { let cc = &bb; }); } 根据上面的分析,不难推导出条件 T: Send + Sync + 'static 的来龙去脉:Closure: Send + 'static ⇒ Arc<T>: Send + ’static ⇒ T: Send + Sync + 'static。 然而,在异步协程代码中有一种常见情况,推导过程则显得比较隐蔽,值得说道说道。考察以下代码: struct AA<T>(T); impl<T> AA<T> { async fn run_self(self) {} async fn run(&self) {} async fn run_mut(&mut self) {} } fn test2<T: Send + 'static>(mut aa: AA<T>) { let ha = async_std::task::spawn(async move { aa.run_self().await; }); } test2 中,限定 T: Send + ‘static,合情合理。async fn 生成的 GenFuture 要求 Send + ‘static,因此被捕获置入 GenFuture 匿名结构中的 AA 也必须满足 Send + ‘static,进而要求AA 泛型参数也满足Send + ‘static。 然而,类似的方式调用 AA::run() 方法,编译失败,编译器提示 GenFuture 不满足 Send。代码如下: fn test2<T: Send + 'static>(mut aa: AA<T>) { let ha = async_std::task::spawn(async move { ^^^^^^^^^^^^^^^^^^^^^^ future returned by `test2` is not `Send` aa.run().await; }); } 原因在于,AA::run()方法的签名是 &self,所以run()是通过 aa 的不可变借用 &AA 来调用。而run()又是一个异步方法,执行了await,也就是所谓的&aa 跨越了 await,故而要求GenFuture匿名结构除了生成aa之外,还需要生成 &aa,示意代码如下: struct { aa: AA aa_ref: &AA } 正如之前探讨过,生成的 GenFuture需要满足 Send,因此 AA 以及 &AA 都需要满足 Send。而&AA满足 Send,则意味着 AA 满足 Sync。这也就是各种 Rust教程中都会提到的那句话的真正含义: 对于任意类型 T,如果 &T是 Send ,T 就是 Sync 的 之前出错的代码修改为如下形式,增加 Sync标记,编译通过。 fn test2<T: Send + Sync + 'static>(mut aa: AA<T>) { let ha = async_std::task::spawn(async move { aa.run().await; }); } 另外,值得指出的是上述代码中调用 AA::run_mut(&mut self) 不需要 Sync 标记: fn test2<T: Send + 'static>(mut aa: AA<T>) { let ha = async_std::task::spawn(async move { aa.run_mut().await; }); } 这是因为 &mut self 并不要求 T: Sync。参见以下标准库中关于Sync定义代码就明白了: mod impls { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: Sync + ?Sized> Send for &T {} #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: Send + ?Sized> Send for &mut T {} } 可以看到,&T: Send 要求 T: Sync,而 &mut T 则 T: Send 即可。 总结 总而言之,Send约束在根源上是由 thread::spawn() 或是 task::spawn() 引入的,因为两个方法的闭包参数必须满足 Send。此外,在需要共享数据时使用Arc<T>会要求 T: Send + Sync。而共享可写数据,需要Arc<Mutex<T>>,此时 T: Send 即可,不再要求Sync。 异步代码中关于 Send/Sync 与同步多线程代码没有不同。只是因为GenFuture 的特别之处使得跨越 await 的变量必须是 T: Send,此时需要注意通过 T 调用异步方法的签名,如果为 &self,则必须满足 T:Send + Sync。 最后,一点经验分享:关于 Send/Sync 的道理并不复杂,更多时候是因为代码中层次比较深,调用关系复杂,导致编译器的错误提示很难看懂,某些特定场合编译器可能还会给出完全错误的修正建议,这时候需要仔细斟酌,追根溯源,找到问题的本质,不能完全依靠编译器提示。 作者:谢敬伟,江湖人称“刀哥”,20年IT老兵,数据通信网络专家,电信网络架构师,目前任Netwarps开发总监。刀哥在操作系统、网络编程、高并发、高吞吐、高可用性等领域有多年的实践经验,并对网络及编程等方面的新技术有浓厚的兴趣。 深圳星链网科科技有限公司(Netwarps),专注于互联网安全存储领域技术的研发与应用,是先进的安全存储基础设施提供商,主要产品有去中心化文件系统(DFS)、企业联盟链平台(EAC)、区块链操作系统(BOS)。 微信公众号:Netwarps

优秀的个人博客,低调大师

C++之友元的学习总结

一、友元的概念: 1、什么是友元? 友元是c++中的一种关系 友元关系发生在函数与类之间或者类与类之间 友元关系是单项的,不能传递 2、友元的用法: 在类中以friend关键字声明友元 类的友元可以是其它类或者具体函数 友元不是类的一部分 友元不受类中访问级别的限制 友元可以直接访问具体类的所有成员 3、友元的语法: 在类中使用friend 关键字对函数或者类进行声明: classTest{doublex;doubley;friendvoidfunc(Test&t);};voidfunc(Test&t){} 注意:func() 全局函数是 Test 类的友元,func() 可以访问Test 类的所有成员,但是 func() 不是 Test 的成员函数。 示例代码: #include<stdio.h>#include<math.h>classTest{doublex;doubley;public:Test(doublex,doubley){this->x=x;this->y=y;}doublegetX(){returnx;}doublegetY(){returny;}frienddoublefunc(Test&t1,Test&t2);};doublefunc(Test&t1,Test&t2){doubleret=0;ret=(t2.y-t1.y)*(t2.y-t1.y)+(t2.x-t1.x)*(t2.x-t1.x);ret=sqrt(ret);returnret;}intmain(){Testt1(1,2);Testt2(10,20);printf("t1(%f,%f)\n",t1.getX(),t1.getY());printf("t2(%f,%f)\n",t2.getX(),t2.getY());printf("(t1,t2)=%f\n",func(t1,t2));return0;} 输出结果: root@txp-virtual-machine:/home/txp/add#g++test.cpproot@txp-virtual-machine:/home/txp/add#./a.outt1(1.000000,2.000000)t2(10.000000,20.000000)(t1,t2)=20.124612 4、友元的尴尬: 友元是为了兼顾c语言的高效而诞生的 友元直接破坏了面向对象的封装性 友元在实际开发产品中的高效是得不偿失的 友元在软件工程中已经慢慢被遗弃了 5、注意事项: 友元关系不具备传递性 类的友元可以是其它类的成员函数 类的友元可以是某个完整的类 所有的成员函数都是友元 代码测试: #include<stdio.h>classClassC{constchar*n;public:ClassC(constchar*n){this->n=n;}friendclassClassB;};classClassB{constchar*n;public:ClassB(constchar*n){this->n=n;}voidgetClassCName(ClassC&c){printf("c.n=%s\n",c.n);}friendclassClassA;};classClassA{constchar*n;public:ClassA(constchar*n){this->n=n;}voidgetClassBName(ClassB&b){printf("b.n=%s\n",b.n);}/*voidgetClassCName(ClassC&c){printf("c.n=%s\n",c.n);}*/};intmain(){ClassAA("A");ClassBB("B");ClassCC("C");A.getClassBName(B);B.getClassCName(C);return0;} 输出结果: root@txp-virtual-machine:/home/txp/add#./a.outb.n=Bc.n=C 如果把上面屏蔽的那部分代码打开,编译就会报错(因为友元没有传递性哦): root@txp-virtual-machine:/home/txp/add#g++test.cpptest.cpp:Inmemberfunction‘voidClassA::getClassCName(ClassC&)’:test.cpp:5:17:error:‘constchar*ClassC::n’isprivateconstchar*n;^test.cpp:48:32:error:withinthiscontextprintf("c.n=%s\n",c.n);^ 6、小结: 友元是为了兼顾c语言的高效而诞生的 友元直接破坏了面向对象的封装性 友元关系不具备传递性 类的友元可以是其它类的成员函数 类的友元可以是某个完成的类 二、总结: 好了,今天的分享就到这里,如果文章中有错误或者不理解的地方,可以交流互动,一起进步。我是txp,下期见! 本文分享自微信公众号 - TXP嵌入式(txp1121518wo-)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册