C++ using:软件设计中的面向对象编程技巧
C++ using:理解头文件与库的使用
- 引言
- using声明
- a. 使用方法和语法
- b. 实际应用场景举例
- i. 避免命名冲突
- ii. 提高代码可读性
- c. 注意事项和潜在风险
- using指令
- a. 使用方法和语法
- b. 实际应用场景举例
- i. 将整个命名空间导入当前作用域
- ii. 代码组织和模块化
- using枚举
- a. C++11的新特性
- b. 使用方法和语法
- c. 实际应用场景举例
- i. 引入枚举值,简化代码
- ii. 提高代码可读性
- d. 注意事项和潜在风险
- using类型别名
- a. 使用方法和语法
- b. 实际应用场景举例
- i. 简化复杂类型的定义
- ii. 提高代码可维护性
- c. 注意事项和潜在风险
- using在模板中的应用
- a. 使用方法和语法
- b. 实际应用场景举例
- i. 简化模板元编程
- ii. 类型推导和类型特性
- c. 注意事项和潜在风险
- 使用建议和最佳实践
- a. 使用using关键字的优缺点分析
- b. 何时应该使用using声明、指令、枚举和类型别名
- c. 使用using关键字的最佳实践和规范
- 总结
引言
C++作为一门具有广泛应用的高级编程语言,自20世纪80年代由Bjarne Stroustrup发明以来,不断地发展和演变。C++在C语言的基础上引入了面向对象编程(OOP)的概念,使得程序员可以编写更为复杂和高效的代码。随着C++98、C++03、C++11、C++14、C++17和C++20等一系列标准的发布,C++不断地引入新特性和优化,以满足不断变化的软件开发需求。
在C++中,命名空间和作用域是两个非常重要的概念。命名空间主要用于组织代码,防止在不同的库或模块中出现命名冲突。C++的命名空间可以嵌套,并且允许在同一个命名空间内声明多个同名实体,但它们必须具有不同的作用域。作用域则定义了程序中变量、函数和对象的可见性和生命周期,控制着程序中不同部分之间的访问权限。
为了简化代码编写和提高代码可读性,C++引入了using关键字。using关键字具有多种用途,包括using声明、using指令、using枚举和using类型别名。通过使用using关键字,程序员可以在不同的作用域中导入命名空间中的实体,避免重复编写冗长的命名空间前缀,同时还可以防止命名冲突。此外,using关键字在模板编程中也发挥着重要作用,可以简化模板元编程,实现类型推导和类型特性等高级功能。
在本文中,我们将详细介绍C++中using关键字的各种用途,语法和实际应用场景,并提供一些最佳实践和使用建议,以帮助程序员编写更为简洁、高效且易于维护的代码。
using声明
a. 使用方法和语法
using声明主要用于将命名空间中的某个特定实体导入到当前作用域,从而避免重复编写命名空间前缀。使用using声明的语法如下:
using namespace_name::entity_name;在这里,namespace_name表示命名空间的名称,entity_name表示要导入的实体名称,如变量、函数、类等。通过using声明,我们可以直接使用实体名称,而不需要加上命名空间前缀。
b. 实际应用场景举例
i. 避免命名冲突
假设我们有两个不同的命名空间,分别包含了相同名称的函数,但它们的实现是不同的。通过使用using声明,我们可以在当前作用域中导入这两个函数,而不会引起命名冲突:
namespace A {void func() {// 实现A版本的func} }namespace B {void func() {// 实现B版本的func} }void main() {using A::func; // 导入A命名空间的funcusing B::func; // 导入B命名空间的funcA::func(); // 调用A命名空间的funcB::func(); // 调用B命名空间的func }ii. 提高代码可读性
在某些情况下,命名空间的名称可能非常长,导致代码变得冗长和难以阅读。通过使用using声明,我们可以简化代码并提高可读性:
#include <iostream>namespace very_long_namespace_name {void print_hello() {std::cout << "Hello, World!" << std::endl;} }int main() {using very_long_namespace_name::print_hello;print_hello(); // 简化代码,提高可读性return 0; }c. 注意事项和潜在风险
虽然using指令可以简化代码并提高可读性,但在使用时也需要注意一些潜在的风险:
- 命名冲突。由于using指令将整个命名空间导入到当前作用域,因此可能导致命名冲突。在使用using指令时,请确保导入的命名空间中的实体名称与当前作用域中的实体名称不冲突。
- 不要滥用using指令。使用using指令会增加代码的隐式依赖,可能导致代码难以理解和维护。适当使用using声明可能是一个更好的选择,因为它只导入所需的实体。
- 避免在头文件中使用using指令。在头文件中使用using指令可能导致不必要的命名冲突,因为头文件可能被多个源文件包含。在头文件中,推荐使用using枚举和using类型别名:
using指令
a. 使用方法和语法
using指令的作用是将整个命名空间导入到当前作用域,这样我们可以直接使用命名空间中的所有实体而无需为它们添加命名空间前缀。使用using指令的语法如下:
using namespace namespace_name;在这里,namespace_name表示要导入的命名空间的名称。
b. 实际应用场景举例
i. 将整个命名空间导入当前作用域
在某些情况下,我们可能需要频繁地使用某个命名空间中的多个实体。通过使用using指令,我们可以将整个命名空间导入当前作用域,以简化代码:
#include <iostream> #include <vector> #include <string>using namespace std;int main() {vector<string> names = {"Alice", "Bob", "Charlie"};for (const auto& name : names) {cout << name << endl;}return 0; }在这个例子中,我们使用了std命名空间中的多个实体(如vector、string和cout等)。通过使用using namespace std;,我们可以直接使用这些实体而无需为它们添加std::前缀。
ii. 代码组织和模块化
在大型项目中,使用命名空间可以帮助我们更好地组织代码。通过使用using指令,我们可以在需要的地方将整个命名空间导入当前作用域,以便更方便地使用其中的实体:
// file: my_library.h namespace my_library {class MyClass {// ...};void my_function() {// ...} }// file: main.cpp #include "my_library.h"using namespace my_library;int main() {MyClass obj;my_function();return 0; }
using枚举
a. C++11的新特性
自C++11起,我们可以使用using枚举来将枚举类型中的枚举值直接导入到当前作用域。这使得我们可以在不加枚举类型名称前缀的情况下直接访问枚举值,从而简化代码和提高可读性。
b. 使用方法和语法
使用using枚举的语法如下:
using enum enum_name;在这里,enum_name表示要导入的枚举类型的名称。
c. 实际应用场景举例
i. 引入枚举值,简化代码
假设我们有一个名为Colors的枚举类型,其中包含了一些颜色的枚举值。通过使用using枚举,我们可以直接访问这些枚举值:
enum class Colors {RED,GREEN,BLUE };void print_color(Colors color) {using enum Colors;switch (color) {case RED:std::cout << "Red" << std::endl;break;case GREEN:std::cout << "Green" << std::endl;break;case BLUE:std::cout << "Blue" << std::endl;break;} }int main() {using enum Colors;print_color(RED); // 输出 "Red"return 0; }ii. 提高代码可读性
在某些情况下,枚举类型的名称可能较长,导致代码变得冗长和难以阅读。通过使用using枚举,我们可以简化代码并提高可读性。
d. 注意事项和潜在风险
使用using枚举时,应注意避免命名冲突。如果当前作用域中已经存在与枚举值同名的实体,使用using枚举可能导致命名冲突。在这种情况下,建议使用完整的枚举类型名称和枚举值来消除歧义。
using类型别名
a. 使用方法和语法
using类型别名可以为复杂的类型定义简洁易懂的别名。使用using类型别名的语法如下:
using alias_name = original_type;在这里,alias_name表示新定义的类型别名,original_type表示原始类型。
b. 实际应用场景举例
i. 简化复杂类型的定义
在某些情况下,类型定义可能非常复杂,导致代码难以阅读。通过使用using类型别名,我们可以为这些复杂类型定义简洁的别名:
#include <map> #include <string>using StringIntMap = std::map<std::string, int>;int main() {StringIntMap my_map;my_map["one"] = 1;my_map["two"] = 2;my_map["three"] = 3;for (const auto& pair : my_map) {std::cout << pair.first << " : " << pair.second << std::endl; }return 0; }在这个例子中,我们使用using类型别名将
std::map<std::string,int>定义为StringIntMap,从而简化了代码并提高了可读性。ii. 提高代码可维护性
使用using类型别名可以使代码更易于维护。如果需要更改原始类型,只需更改类型别名的定义,而无需在整个代码库中进行搜索和替换。这样可以大大减少错误和遗漏的风险。
c. 注意事项和潜在风险
使用using类型别名时,应注意以下几点:
- 为类型别名选择有意义的名称。使用简洁且易懂的名称可以提高代码的可读性。
- 避免过度使用类型别名。虽然类型别名可以简化代码,但过度使用可能导致代码结构混乱。在合适的场景下使用类型别名,以保持代码清晰和易于理解。
- 在适当的作用域中定义类型别名。为了避免命名冲突和全局污染,建议在需要使用类型别名的作用域中定义它们。
using在模板中的应用
a. 使用方法和语法
在C++模板编程中,using关键字可以用于定义模板类型别名和模板嵌套类型。使用using关键字定义模板类型别名的语法如下:
template<typename T> using alias_name = some_template<T>;在这里,alias_name表示模板类型别名,some_template表示原始模板类型,T表示模板参数。
b. 实际应用场景举例
i. 简化模板元编程
在模板元编程中,using关键字可以用于简化复杂的模板表达式。例如,使用std::conditional模板来根据条件选择类型:
#include <type_traits>template<bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;template<typename T> using add_pointer_t = typename std::add_pointer<T>::type;template<typename T> struct MyTemplate {using type = conditional_t<std::is_integral<T>::value, add_pointer_t<T>, T>; };int main() {MyTemplate<int>::type x; // x的类型为int*MyTemplate<double>::type y; // y的类型为double }ii. 类型推导和类型特性
使用using关键字可以帮助我们更容易地进行类型推导和操作类型特性。例如,我们可以使用std::enable_if来实现SFINAE(Substitution Failure Is Not An Error)技术:
#include <iostream> #include <type_traits>template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>> void foo(const T& value) {std::cout << "Integral type: " << value << std::endl; }template<typename T, typename = std::enable_if_t<!std::is_integral<T>::value>> void foo(const T& value) {std::cout << "Non-integral type: " << value << std::endl; }int main() {foo(42); // 输出 "Integral type: 42"foo(3.14); // 输出 "Non-integral type: 3.14" }c. 注意事项和潜在风险
在模板编程中使用using关键字时,也需要注意一些潜在的风险:
- 在模板中正确使用using关键字。请确保在模板中正确地使用using关键字,遵循模板类型别名和嵌套类型的语法规则。
- 避免过度复杂化。尽管using关键字可以简化模板元编程,但过于复杂的模板表达式可能导致代码难以理解和维护。在合适的场景下使用using关键字,并保持代码清晰和易于理解。
- 注意编译时错误。模板编程可能导致复杂的编译时错误。当使用using关键字时,请确保编译时错误能够被合理地处理,并给出有意义的错误信息。
使用建议和最佳实践
a. 使用using关键字的优缺点分析
优点:
- 提高代码可读性:using关键字可以简化代码,使得代码更加简洁和易读。
- 避免命名冲突:通过使用命名空间和using声明,可以减少命名冲突的风险。
提高代码可维护性:使用using类型别名可以使代码更易于维护,减少因修改类型而引入的错误。缺点:
- 命名冲突:使用using指令时,可能导致命名冲突,特别是在全局作用域内。
- 隐式依赖:使用using关键字可能导致代码之间的隐式依赖,使代码难以理解和维护。
b. 何时应该使用using声明、指令、枚举和类型别名
- 使用using声明:在需要导入特定命名空间中的一个或几个实体时,使用using声明是一个不错的选择。这样可以避免整个命名空间的导入,减少命名冲突的风险。
- 使用using指令:在需要频繁使用某个命名空间中的实体时,可以考虑使用using指令。但需要注意避免命名冲突,特别是在包含头文件的情况下。
- 使用using枚举:当需要将枚举类型的枚举值直接导入到当前作用域以简化代码和提高可读性时,可以使用using枚举。
- 使用using类型别名:为复杂类型定义简洁易懂的别名,以提高代码可读性和可维护性。
c. 使用using关键字的最佳实践和规范
避免在全局作用域中使用using指令:在全局作用域中使用using指令可能导致命名冲突和全局污染。建议在局部作用域或函数内部使用using指令。
在头文件中谨慎使用using关键字:避免在头文件中使用using指令,因为它可能导致不必要的命名冲突。在头文件中,推荐使用using声明或完全限定的名称。
使用有意义的类型别名:为类型别名选择简洁且具有描述性的名称,以提高代码的可读性和可维护性。
保持代码清晰和易于理解:在使用using关键字时,应注意保持代码清晰和易于理解。避免使用过于复杂的模板表达式,以防止代码变得难以阅读和维护。
遵循项目规范和编码标准:在使用using关键字时,应遵循项目规范和编码标准。这将有助于保持代码一致性,使团队成员更容易理解和维护代码。
使用using类型别名替换typedef:在C++11及更高版本中,建议使用using类型别名替换typedef,因为using类型别名更具可读性,且可以处理模板类型别名。
限制using声明的使用范围:只有在确实需要时才使用using声明。避免在不必要的地方引入实体,这将有助于减少命名冲突的风险。
谨慎使用using枚举:在使用using枚举时,注意避免命名冲突。如果当前作用域中已经存在与枚举值同名的实体,使用using枚举可能导致命名冲突。在这种情况下,建议使用完整的枚举类型名称和枚举值来消除歧义。
总之,C++中的using关键字具有多种用途,如using声明、using指令、using枚举和using类型别名。
遵循最佳实践和规范,可以充分利用using关键字的优势,编写简洁、高效且易于维护的代码。
在使用过程中,务必注意潜在的风险,如命名冲突、隐式依赖等,从而确保代码质量。
总结
C++中的using关键字具有多种用途,如using声明、using指令、using枚举和using类型别名。
在不同的应用场景中,using关键字可以简化代码、提高代码可读性、避免命名冲突以及提高代码可维护性。
然而,在使用using关键字时,也需要注意潜在的风险,如命名冲突、滥用using声明或指令等。
通过遵循最佳实践和使用建议,程序员可以充分利用using关键字的优势,编写更为简洁、高效且易于维护的代码。
相关文章:
C++ using:软件设计中的面向对象编程技巧
C using:理解头文件与库的使用引言using声明a. 使用方法和语法b. 实际应用场景举例i. 避免命名冲突ii. 提高代码可读性c. 注意事项和潜在风险using指令a. 使用方法和语法b. 实际应用场景举例i. 将整个命名空间导入当前作用域ii. 代码组织和模块化using枚举a. C11的新特性b. 使用…...
修建灌木顺子日期
题目 有 N 棵灌木整齐的从左到右排成一排。爱丽丝在每天傍晩会修剪一棵灌 木, 让灌木的高度变为 0 厘米。爱丽丝修剪灌木的顺序是从最左侧的灌木开始, 每天向右修剪一棵灌木。当修剪了最右侧的灌木后, 她会调转方向, 下一天开 始向左修剪灌木。直到修剪了最左的灌木后再次调转方…...
深入学习JavaScript系列(七)——Promise async/await generator
本篇属于本系列第七篇 第一篇:#深入学习JavaScript系列(一)—— ES6中的JS执行上下文 第二篇:# 深入学习JavaScript系列(二)——作用域和作用域链 第三篇:# 深入学习JavaScript系列ÿ…...
Mybatis中的Map的使用和模糊查询的需求实现及其防SQL注入优化
文章目录一.Map的使用和模糊查询的需求实现及其防SQL注入优化1.1 Map的使用1.2 模糊查询的实现1.2.1 防SQL注入优化1.2.2 总结一.Map的使用和模糊查询的需求实现及其防SQL注入优化 1.1 Map的使用 替换之前的根据ID查询信息: 1.编写接口: User getUse…...
【redis】redis缓存更新策略
目录一、缓存更新策略二、主动更新策略三、Cache Aside Pattern3.1 删除缓存还是更新缓存?3.2 如何保证缓存与数据库的操作同时成功或失败?3.3 先操作缓存还是先操作数据库3.3.1 先删缓存再删库3.3.2 先删库再删缓存一、缓存更新策略 1.内存淘汰:不用自…...
LeetCode刷题--复制带随机指针的链表
复制带随机指针的链表1.题目2.解题思路3.完整代码1.题目 题目链接: https://leetcode.cn/problems/copy-list-with-random-pointer/ 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。 …...
关于我的第一台电脑 华硕
2011年买的,第一台电脑是华硕 U36KI243SD 13.3英寸 白色 i5 1G独显 USB3.0 500G 当时花了5699,着实是一笔巨款,我同学看了一眼就说“我C,这本真好”。 买它主要还是因为好看。当时win7也才开始流行,感觉用上这个本&…...
【华为OD机试 2023最新 】 最大化控制资源成本(C++ 100%)
文章目录 题目描述输入描述输出描述备注用例题目解析C++题目描述 公司创新实验室正在研究如何最小化资源成本,最大化资源利用率,请你设计算法帮他们解决一个任务混部问题: 有taskNum项任务,每个任务有开始时间(startTime),结束时间(endTime),并行度(parallelism)…...
leetcode 有序数组的平方(977)
题目 给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。 示例 1: 输入:nums [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变…...
文本三剑客之awk
文本三剑客之awkawk命令的简要处理流程awk命令的执行过程NR输出分割符和输入分割符案例awk命令引用shell变量awk的几个内置函数流控数组awk命令的简要处理流程 awk命令的执行过程 awk BEGIN{commands} pattern{commands} END{commands}files 执行BEGIN {commands}语句块中的语…...
RK3568平台开发系列讲解(驱动基础篇)IS_ERR函数的使用
🚀返回专栏总目录 文章目录 一、IS_ERR函数二、内核错误码沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍 IS_ERR 函数的使用。 一、IS_ERR函数 对于任何一个指针来说,必然存在三种情况: 一种是合法指针一种是 NULL (也就是空指针)一种是错误指针(也就…...
特殊的类之注解
注解🚙注解的入门和作用以及原理示例注解的方法名就是属性名Retention的作用Target的作用注解的属性设置默认值天生我材必有用,千金散尽还复来。——唐代李白《将进酒》 在Java中,注解实际上是特殊类型的接口,当我们使用注解时&am…...
商业分享:盲盒电商开启电商新可能
盲盒,顾名思义,一个看不出里面装着什么东西的盒子。当你看不见盲盒里的商品时,你会思考盲盒里可能装着什么,它会诱发你的好奇心,而在好奇心的促使下,你会不由自主地买一个拆开来看,刚好大多数盲…...
【计算机架构】如何计算 CPU 时间
目录 0x00 响应时间和吞吐量(Response Time and Throughput) 0x01 相对性能(Relative Performance) 0x02 执行时间测量(Measuring Execution Time) 0x03 CPU 时钟(Clocking) 0x…...
银行数字化转型导师坚鹏:银行行长如何进行数字化转型
银行行长如何进行数字化转型 ——数字化转型背景下重塑银行行长核心竞争力 授课背景: 很多银行存在以下问题:银行行长不知道如何进行数字化转型?银行行长不清楚银行数字化能力模型的内涵?银行行长不知道如何通过数字化转型提…...
N32G45x学习笔记--- gpio引脚复用
关于gpio的引脚复用需要开启复用时钟 RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);官方引脚复用: 芯片上电默认使能 SWD-JTAG 调试接口,调试接口被映射到 GPIO 端口上 禁止 JTAG 使能SWJ-DP /* 禁止 JTAG 使能SWJ-DP */GPIO_ConfigPinRemap(GPIO_RMP_SW_JTAG_S…...
ArcGIS Pro中使用深度学习的高分辨率土地覆盖制图
本文非常详细的讲解了利用深度学习在高分辨率土地覆盖制图的应用,本文作者:Amin Tayyebi,文章从数据准备到训练U-Net模型等等细节都有讲解。本译文只是使用谷歌翻译而成。文章可能有错误语句及不通顺情况,所以仅供参考学习。有需要…...
【学习笔记】「NOI2018」冒泡排序
从题解的角度来说,这是一道简单题。不过考场上在没有任何人提示的情况下要想出正确的结论其实并不容易。 我自己做这道题的时候,因为没有想清楚题目给出的下界能取到的充要条件是什么,所以到了很晚才猜到结论,以至于难以为继。 …...
【Ruby学习笔记】3.Ruby 语法及数据类型
前言 本章介绍Ruby的语法和数据类型。 Ruby 语法 让我们编写一个简单的 Ruby 程序。所有的 Ruby 文件扩展名都是 .rb。所以,把下面的源代码放在 test.rb 文件中。 实例 #!/usr/bin/ruby -wputs "Hello, Ruby!";在这里,假设您的 /usr/bin …...
华为OD机试题【字符匹配】用 Java 解 | 含解题说明
华为Od必看系列 华为OD机试 全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典本篇题目:字符匹配 题目 给你一个字符串…...
BepInEx Linux部署实战指南:从环境诊断到故障自愈
BepInEx Linux部署实战指南:从环境诊断到故障自愈 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx 一、环境诊断:你的Linux系统准备好了吗? 为什…...
51单片机(九)—— 数码管动态扫描原理与实现
1. 数码管动态扫描原理揭秘 第一次接触多位数码管显示时,我盯着电路板百思不得其解:明明只有8个数据引脚,怎么能同时控制8位数码管显示不同内容?直到理解了动态扫描原理,才恍然大悟这背后的精妙设计。动态扫描本质上是…...
Gemini Advanced 2025生产力跃迁:从入门到精通的场景化应用手册
1. Gemini Advanced 2025入门指南:从零开始的AI生产力工具 第一次打开Gemini Advanced时,我完全被它的界面简洁性震惊了——没有复杂的菜单,只有一个干净的对话框。但别被这简单外表迷惑,这个AI助手能做的事情远超想象。对于刚接触…...
Mac用户必看:Homebrew换源提速全攻略(附清华镜像最新配置)
Mac开发者必备:Homebrew国内镜像加速终极指南 每次打开终端准备用Homebrew安装新工具时,那个缓慢的下载进度条是否让你抓狂?作为Mac生态中最受欢迎的包管理工具,Homebrew的默认服务器位于海外,国内用户常遭遇下载速度以…...
Docker vs Pip:MinerU本地部署全攻略,哪种方式更适合你的PDF解析需求?
Docker与Pip部署MinerU深度对比:如何为PDF解析选择最佳方案 在文档自动化处理领域,PDF解析工具的选择往往直接影响工作效率。MinerU作为一款开源的PDF解析工具,因其对复杂排版的良好支持而受到开发者青睐。但面对Pip和Docker两种主流部署方式…...
Hunyuan-MT Pro详细步骤:本地启动http://localhost:6666翻译终端
Hunyuan-MT Pro详细步骤:本地启动http://localhost:6666翻译终端 1. 快速了解Hunyuan-MT Pro Hunyuan-MT Pro是一个基于腾讯混元开源模型构建的现代化翻译工具,它把强大的AI翻译能力包装成了一个简单易用的网页应用。你不需要懂复杂的技术,…...
PyTorch模型参数与元数据安全存储:safetensors实战解析
1. 为什么需要safetensors存储模型参数? 在深度学习项目中,模型参数的保存和加载是最基础也最频繁的操作。传统PyTorch开发者习惯使用torch.save和torch.load这对黄金组合,直到某天我在分布式训练集群上遇到了一个诡异的问题:一个…...
Intel Broadwell处理器选型指南:IBRS、noTSX这些后缀到底该怎么选?
Intel Broadwell处理器选型实战:从安全特性到性能优化的深度解析 在2014年问世的Intel Broadwell架构,作为第五代酷睿处理器的重要里程碑,至今仍在特定应用场景中保持着独特的价值。不同于简单的参数对比,本文将带您深入理解不同…...
掌握微信聊天记录永久备份:从数据主权到智能记忆管理
掌握微信聊天记录永久备份:从数据主权到智能记忆管理 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeChat…...
老Mac升级指南:使用OpenCore Legacy Patcher让旧设备焕发新生
老Mac升级指南:使用OpenCore Legacy Patcher让旧设备焕发新生 【免费下载链接】OpenCore-Legacy-Patcher 体验与之前一样的macOS 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 随着苹果对旧款Mac的系统支持逐渐终止࿰…...
