当前位置: 首页 > news >正文

设计模式之单例模式(C++)

作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

一、单例模式是什么?

       单例模式是一种创建型的软件设计模式,在工程项目中非常常见。通过单例模式的设计,使得创建的类在当前进程中只有一个实例,并提供一个全局性的访问点,这样可以规避因频繁创建对象而导致的内存飙升情况。

       实现单例模式的三个要点:

1)私有化构造函数:这样外界就无法自由地创建类对象,进而阻止了多个实例的产生。

2)类定义中含有该类的唯一静态私有对象:静态变量存放在全局存储区,且是唯一的,供所有对象使用。

3)用公有的静态函数来获取该实例:提供了访问接口。

       单例模式一般分为懒汉式和饿汉式。

1)懒汉式:在使用类对象(单例实例)时才会去创建它,不然就懒得去搞。

2)饿汉式:单例实例在类装载时构建,有可能全局都没使用过,但它占用了空间,就像等着发救济粮的饿汉提前排好队等吃的一样。

二、懒汉式实现

2.1 懒汉基础实现

       最基本的懒汉实现方法。

//Singleton.h
/****************************************************/
#include <iostream>using namespace std;// 单例模式演示类
class Singleton
{
public:// 公有接口获取唯一实例static Singleton* getInstance(){// 若为空则创建if (instance == nullptr) {cout << "实例为空,开始创建。" << endl;instance = new Singleton();cout << "创建结束。" << endl;}else {cout << "已有实例,返回。" << endl;}return instance;}
private:// 私有构造函数Singleton(){cout << "构造函数启动。" << endl;};// 私有析构函数~Singleton(){cout << "析构函数启动。" << endl;};
private:// 静态私有对象static Singleton* instance;
};// 初始化
Singleton* Singleton::instance = nullptr;
//main.h
/****************************************************/
#include <iostream>
#include "Singleton.h"using namespace std;int main() 
{cout << "main开始" << endl;Singleton* s1 = Singleton::getInstance();Singleton* s2 = Singleton::getInstance();Singleton* s3 = Singleton::getInstance();cout << "main结束" << endl;return 0;
}

       执行代码,让我们看看结果。

       从结果中可以看出这样设计主要有两个问题,一个是线程安全,另一个是内存泄漏。

       线程安全是因为在多线程场景下,有可能出现多个线程同时进行new操作的情况,没通过加锁来限制。

       内存泄漏是因为使用了new在堆上分配了资源,那么在程序结束时,也应该进行delete,确保堆中数据释放。

       接下来,我们先解决线程安全问题,对懒汉式实现进行改进。

2.2 基于双重检测锁的懒汉实现

       通过双重检测锁,可以确保线程安全。

//Singleton.h
/****************************************************/
#include <iostream>
#include <mutex>using namespace std;// 单例模式演示类
class Singleton
{
public:// 公有接口获取唯一实例static Singleton* getInstance(){// 若为空则创建if (instance == nullptr) {// 加锁保证线程安全// 如果两个线程同时进行到这一步,一个线程继续向下执行时,另一个线程被堵塞// 等锁解除后,被堵塞的线程就会跳过下面的if了,因为此时实例已经构建完毕lock_guard<mutex> l(m_mutex);if (instance == nullptr) {cout << "实例为空,开始创建。" << endl;instance = new Singleton();cout << "地址为:" << instance << endl;cout << "创建结束。" << endl;}}else {cout << "已有实例,返回。" << endl;}return instance;}
private:// 私有构造函数Singleton(){cout << "构造函数启动。" << endl;};// 私有析构函数~Singleton(){cout << "析构函数启动。" << endl;};
private:// 静态私有对象static Singleton* instance;// 锁static mutex m_mutex;
};// 初始化
Singleton* Singleton::instance = nullptr;
mutex Singleton::m_mutex;
//main.h
/****************************************************/
#include <iostream>
#include "Singleton.h"using namespace std;int main() 
{cout << "main开始" << endl;thread t1([] {Singleton* s1 = Singleton::getInstance();});thread t2([] {Singleton* s2 = Singleton::getInstance();});t1.join();t2.join();Singleton* s3 = Singleton::getInstance();cout << "地址为:" << s3 << endl;cout << "main结束" << endl;return 0;
}

       执行代码,让我们看看结果。

       这样看来没有问题,那如果取消双重检测锁,在多线程下看看会发生什么。将代码部分函数修改为下。把锁注释掉,再查看地址信息。

// 公有接口获取唯一实例
static Singleton* getInstance(){// 若为空则创建if (instance == nullptr) {// 加锁保证线程安全// 如果两个线程同时进行到这一步,一个线程继续向下执行时,另一个线程被堵塞// 等锁解除后,被堵塞的线程就会跳过下面的if了,因为此时实例已经构建完毕//lock_guard<mutex> l(m_mutex);if (instance == nullptr) {cout << "实例为空,开始创建。" << endl;instance = new Singleton();cout << "地址为:" << instance << endl;cout << "创建结束。" << endl;}}else {cout << "已有实例,返回。" << endl;}return instance;
}

       此时结果中可以看出,两个线程进行了两次new操作,但是最后只能捕捉到最后一次new的地址信息了,前面的那个丢失了。。。。。

       这个测试也是让大家直观地感受下双重检测锁的用处。

       接下来,我们再解决内存泄漏(资源释放)问题,对懒汉式实现进行进一步的改进。

2.3 基于双重检测锁和资源管理的懒汉实现

       在2.2的基础上,我们加入资源管理机制,以达到对资源的释放的目的,解决方法有两个:智能指针&静态嵌套类。

2.3.1 智能指针方案

       将实例指针更换为智能指针,另外智能指针在初始化时,还需要人为添加公有的毁灭函数,因为析构函数私有化了。

//Singleton.h
/****************************************************/
#include <iostream>
#include <mutex>using namespace std;// 单例模式演示类
class Singleton
{
public:// 公有接口获取唯一实例static shared_ptr<Singleton> getInstance(){// 若为空则创建if (instance == nullptr) {// 加锁保证线程安全// 如果两个线程同时进行到这一步,一个线程继续向下执行时,另一个线程被堵塞// 等锁解除后,被堵塞的线程就会跳过下面的if了,因为此时实例已经构建完毕lock_guard<mutex> l(m_mutex);if (instance == nullptr) {cout << "实例为空,开始创建。" << endl;instance.reset(new Singleton(), destoryInstance);cout << "地址为:" << instance << endl;cout << "创建结束。" << endl;}}else {cout << "已有实例,返回。" << endl;}return instance;}// 毁灭实例static void destoryInstance(Singleton* x) {cout << "自定义释放实例" << endl;delete x;}
private:// 私有构造函数Singleton(){cout << "构造函数启动。" << endl;};// 私有析构函数~Singleton(){cout << "析构函数启动。" << endl;};
private:// 静态私有对象static shared_ptr<Singleton> instance;// 锁static mutex m_mutex;
};// 初始化
shared_ptr<Singleton> Singleton::instance;
mutex Singleton::m_mutex;
//main.h
/****************************************************/
#include <iostream>
#include "Singleton.h"using namespace std;int main() 
{cout << "main开始" << endl;thread t1([] {shared_ptr<Singleton> s1 = Singleton::getInstance();});thread t2([] {shared_ptr<Singleton> s2 = Singleton::getInstance();});t1.join();t2.join();shared_ptr<Singleton> s3 = Singleton::getInstance();cout << "地址为:" << s3 << endl;cout << "main结束" << endl;return 0;
}

       应用智能指针后,在程序结束时,它自动进行资源的释放,解决了内存泄漏的问题。

2.3.2 静态嵌套类方案

       类中定义一个嵌套类,初始化该类的静态对象,当程序结束时,该对象进行析构的同时,将单例实例也删除了。

//Singleton.h
/****************************************************/
#include <iostream>
#include <mutex>using namespace std;// 单例模式演示类
class Singleton
{
public:// 公有接口获取唯一实例static Singleton* getInstance() {// 若为空则创建if (instance == nullptr) {// 加锁保证线程安全// 如果两个线程同时进行到这一步,一个线程继续向下执行时,另一个线程被堵塞// 等锁解除后,被堵塞的线程就会跳过下面的if了,因为此时实例已经构建完毕lock_guard<mutex> l(m_mutex);if (instance == nullptr) {cout << "实例为空,开始创建。" << endl;instance = new Singleton();cout << "地址为:" << instance << endl;cout << "创建结束。" << endl;}}else {cout << "已有实例,返回。" << endl;}return instance;}
private:// 私有构造函数Singleton() {cout << "构造函数启动。" << endl;};// 私有析构函数~Singleton() {cout << "析构函数启动。" << endl;};// 定义一个删除器class Deleter {public:Deleter() {};~Deleter() {if (instance != nullptr) {cout << "删除器启动。" << endl;delete instance;instance = nullptr;}}};// 删除器是嵌套类,当该静态对象销毁的时候,也会将单例实例销毁static Deleter m_deleter;
private:// 静态私有对象static Singleton* instance;// 锁static mutex m_mutex;
};// 初始化
Singleton* Singleton::instance = nullptr;
mutex Singleton::m_mutex;
Singleton::Deleter Singleton::m_deleter;

      main.h同2.2中的一致,结果如下,可以看出,当嵌套类Deleter对象销毁时,其析构函数执行的实例删除操作也完成了。

2.4 基于局部静态对象的懒汉实现

       C++11后,规定了局部静态对象在多线程场景下的初始化行为,只有在首次访问时才会创建实例,后续不再创建而是获取。若未创建成功,其他的线程在进行到这步时会自动等待。注意C++11前的版本不是这样的。

       因为有上述的改动,所以出现了一种更简洁方便优雅的实现方法,基于局部静态对象实现。

//Singleton.h
/****************************************************/
#include <iostream>
#include <mutex>using namespace std;// 单例模式演示类
class Singleton
{
public:// 公有接口获取唯一实例static Singleton& getInstance() {cout << "获取实例" << endl;static Singleton instance;cout << "地址为:" << &instance << endl;return instance;}
private:// 私有构造函数Singleton() {cout << "构造函数启动。" << endl;};// 私有析构函数~Singleton() {cout << "析构函数启动。" << endl;};
};
//main.h
/****************************************************/
#include <iostream>
#include "Singleton.h"using namespace std;int main()
{cout << "main开始" << endl;thread t1([] {Singleton &s1 = Singleton::getInstance();});thread t2([] {Singleton &s2 = Singleton::getInstance();});t1.join();t2.join();cout << "main结束" << endl;return 0;
}

       从结果中可以看出,构造函数启动了一次,另一个线程直接获取了地址。并且当程序结束时,进行了自动释放。

三、饿汉式实现

3.1 饿汉基础实现

       饿汉和懒汉的差别就在于,饿汉提前进行了创建,所以它的基础实现也不是很复杂,如下所示。

//Singleton.h
/****************************************************/
#include <iostream>
#include <mutex>using namespace std;// 单例模式演示类
class Singleton
{
public:// 公有接口获取唯一实例static Singleton* getInstance() {cout << "获取实例" << endl;cout << "地址为:" << instance << endl;return instance;}
private:// 私有构造函数Singleton() {cout << "构造函数启动。" << endl;};// 私有析构函数~Singleton() {cout << "析构函数启动。" << endl;};
private:// 静态私有对象static Singleton* instance;
};// 初始化
Singleton* Singleton::instance = new Singleton();
//main.h
/****************************************************/
#include <iostream>
#include "Singleton.h"using namespace std;int main()
{cout << "main开始" << endl;thread t1([] {Singleton* s1 = Singleton::getInstance();});thread t2([] {Singleton* s2 = Singleton::getInstance();});t1.join();t2.join();cout << "main结束" << endl;return 0;
}

       输出结果中可知,main还没开始,实例就已经构建完毕,获取实例的函数也不需要进行判空操作,因此也就不用双重检测锁来保证线程安全了,它本身已经是线程安全状态了。

       但是内存泄漏的问题还是要解决的,这点同懒汉是一样的。

3.2 基于资源管理的饿汉实现

       内存泄漏解决方法有两个:智能指针&静态嵌套类。

3.2.1 智能指针方案

       将实例指针更换为智能指针,另外智能指针在初始化时,还需要人为添加公有的毁灭函数,因为析构函数私有化了。

//Singleton.h
/****************************************************/
#include <iostream>
#include <mutex>using namespace std;// 单例模式演示类
class Singleton
{
public:// 公有接口获取唯一实例static shared_ptr<Singleton> getInstance() {cout << "获取实例" << endl;cout << "地址为:" << instance << endl;return instance;}// 毁灭实例static void destoryInstance(Singleton* x) {cout << "自定义释放实例" << endl;delete x;}
private:// 私有构造函数Singleton() {cout << "构造函数启动。" << endl;};// 私有析构函数~Singleton() {cout << "析构函数启动。" << endl;};
private:// 静态私有对象static shared_ptr<Singleton> instance;
};// 初始化
shared_ptr<Singleton> Singleton::instance(new Singleton(), destoryInstance);
//main.h
/****************************************************/
#include <iostream>
#include "Singleton.h"using namespace std;int main()
{cout << "main开始" << endl;thread t1([] {shared_ptr<Singleton> s1 = Singleton::getInstance();});thread t2([] {shared_ptr<Singleton> s2 = Singleton::getInstance();});t1.join();t2.join();cout << "main结束" << endl;return 0;
}

       加入了智能指针后,不出意外地进行了自动的资源释放。

3.2.2 静态嵌套类方案

       类中定义一个嵌套类,初始化该类的静态对象,当程序结束时,该对象进行析构的同时,将单例实例也删除了。

//Singleton.h
/****************************************************/
#include <iostream>
#include <mutex>using namespace std;// 单例模式演示类
class Singleton
{
public:// 公有接口获取唯一实例static Singleton* getInstance() {cout << "获取实例" << endl;cout << "地址为:" << instance << endl;return instance;}
private:// 私有构造函数Singleton() {cout << "构造函数启动。" << endl;};// 私有析构函数~Singleton() {cout << "析构函数启动。" << endl;};// 定义一个删除器class Deleter {public:Deleter() {};~Deleter() {if (instance != nullptr) {cout << "删除器启动。" << endl;delete instance;instance = nullptr;}}};// 删除器是嵌套类,当该静态对象销毁的时候,也会将单例实例销毁static Deleter m_deleter;
private:// 静态私有对象static Singleton* instance;
};// 初始化
Singleton* Singleton::instance = new Singleton();
Singleton::Deleter Singleton::m_deleter;
//main.h
/****************************************************/
#include <iostream>
#include "Singleton.h"using namespace std;int main()
{cout << "main开始" << endl;thread t1([] {Singleton* s1 = Singleton::getInstance();});thread t2([] {Singleton* s2 = Singleton::getInstance();});t1.join();t2.join();cout << "main结束" << endl;return 0;
}

       同懒汉的一样,不多做阐述。

四、总结

       上述讲了这么多关于单例模式的内容,我尽可能地将测试的结果也同步展示了,目的就是帮助大家更好地理解。文中所有的代码都是完整的,可以直接复制到自己的项目中测试验证下。

       最后,如果说让我选择用什么样的实现,那我选择用局部静态对象的方法,代码简洁,线程安全,内存无泄漏,有什么理由说不呢?除非你是C++11之前的版本。。。。

       如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

相关文章:

设计模式之单例模式(C++)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 一、单例模式是什么&#xff1f; 单例模式是一种创建型的软件设计模式&#xff0c;在工程项目中非常常见。通过单例模式的设计&am…...

贪心算法(基础)

目录 一、什么是贪心&#xff1f; &#xff08;一&#xff09;以教室调度问题为例 1. 问题 2. 具体做法如下 3. 因此将在这间教室上如下三堂课 4. 结论 &#xff08;二&#xff09;贪心算法介绍 1. 贪心算法一般解题步骤 二、最优装载问题 &#xff08;一&#xf…...

【九宫格坐标排列 Objective-C语言】

一、这个案例做好之后的效果如图: 1.这个下载是可以点击的,当你点击之后,弹出一个框,过一会儿,框框自动消失,这里变成“已安装” 2.那么,我现在先问大家一句话:大家认为在这一个应用里面,它包含几个控件, 3个,哪3个:一个是图片框,一个是Label,一个是按钮, 这…...

Tomcat简介

目录 一、Tomcat简介 二、下载安装Tomcat 三、利用Tomcat部署静态页面 一、Tomcat简介 Tomcat是一个HTTP服务器&#xff0c;可以按照HTTP的格式来解析请求来调用用户指定的相关代码然后按照HTTP的格式来构造返回数据。 二、下载安装Tomcat 进入Tomcat官网选择与自己电脑…...

Python基础及函数解读(深度学习)

一、语句1.加注释单行注释&#xff1a;&#xff08;1&#xff09;在代码上面加注释&#xff1a; # 后面跟一个空格&#xff08;2&#xff09;在代码后面加注释&#xff1a;和代码相距两个空格&#xff0c; # 后面再跟一个空格多行注释&#xff1a;按住shift 点击三次"&am…...

车道线检测-PolyLaneNet 论文学习笔记

论文&#xff1a;《PolyLaneNet: Lane Estimation via Deep Polynomial Regression》代码&#xff1a;https://github.com/lucastabelini/PolyLaneNet地址&#xff1a;https://arxiv.org/pdf/2004.10924.pdf参考&#xff1a;https://blog.csdn.net/sinat_17456165/article/deta…...

GO——接口(下)

接口接口值警告&#xff1a;一个包含空指针值的接口不是nil接口sort.Interface接口http.Handler接口类型断言类型分支接口值 接口值&#xff0c;由两个部分组成&#xff0c;一个具体的类型和那个类型的值。它们被称为接口的动态类型和动态值。对于像Go语言这种静态类型的语言&…...

计算机网络之http02| HTTPS HTTP1.1的优化

post与get请求的区别 get 是获取资源&#xff0c;Post是向指定URI提交资源&#xff0c;相关信息放在body里 2.http有哪些优点 &#xff08;1&#xff09;简单 报文只有报文首部和报文主体&#xff0c;易于理解 &#xff08;2&#xff09;灵活易拓展 URI相应码、首部字段都没有…...

基于matlab使用神经网络清除海杂波

一、前言此示例演示如何使用深度学习工具箱™训练和评估卷积神经网络&#xff0c;以消除海上雷达 PPI 图像中的杂波返回。深度学习工具箱提供了一个框架&#xff0c;用于设计和实现具有算法、预训练模型和应用程序的深度神经网络。二、数据集该数据集包含 84 对合成雷达图像。每…...

每天10个前端小知识 【Day 8】

前端面试基础知识题 1. Javascript中如何实现函数缓存&#xff1f;函数缓存有哪些应用场景&#xff1f; 函数缓存&#xff0c;就是将函数运算过的结果进行缓存。本质上就是用空间&#xff08;缓存存储&#xff09;换时间&#xff08;计算过程&#xff09;&#xff0c; 常用于…...

【项目精选】基于Java的敬老院管理系统的设计和实现

本系统主要是针对敬老院工作人员即管理员和员工设计的。敬老院管理系统 将IT技术为养老院提供一个接口便于管理信息,存储老人个人信息和其他信息,查找 和更新信息的养老院档案,节省了员工的劳动时间,大大降低了成本。 其主要功能包括&#xff1a; 系统管理员用户功能介绍&#…...

Spark SQL 介绍

文章目录Spark SQL1、Hive on SparkSQL2、SparkSQL 优点3、SparkSQL 特点1) 容易整合2) 统一的数据访问3) 兼容 Hive4) 标准的数据连接4、DataFrame 是什么5、DataSet 是什么Spark SQL Spark SQL 是 Spark 用于结构化数据(structured data) 处理的Spark模块。 1、Hive on Spa…...

升级到 CDP 后Hive on Tez 性能调整和故障排除指南

优化Hive on Tez查询永远不能以一种万能的方法来完成。查询的性能取决于数据的大小、文件类型、查询设计和查询模式。在性能测试期间&#xff0c;要评估和验证配置参数和任何 SQL 修改。建议在工作负载的性能测试期间一次进行一项更改&#xff0c;并且最好在生产环境中使用它们…...

理解HDFS工作流程与机制,看这篇文章就够了

HDFS(The Hadoop Distributed File System) 是最初由Yahoo提出的分布式文件系统&#xff0c;它主要用来&#xff1a; 1&#xff09;存储大数据 2&#xff09;为应用提供大数据高速读取的能力 重点是掌握HDFS的文件读写流程&#xff0c;体会这种机制对整个分布式系统性能提升…...

Intel处理器分页机制

分页模式 Intel 64位处理器支持3种分页模式&#xff1a; 32-bit分页PAE分页IA-32e分页 32-bit分页 32-bit分页模式支持两种页面大小&#xff1a;4KB以及4MB。 4KB页面的线性地址转换 4MB页面的线性地址转换 PAE分页模式 PAE分页模式支持两种页面大小&#xff1a;4KB以及…...

Linux常用命令

linux常用命令创建一个目录mkdir 命令可以创建新目录。mkdir 是 make directory 的缩写。[rootiZ2ze66tzux2otcpbvie88Z ~]# ls [rootiZ2ze66tzux2otcpbvie88Z ~]# mkdir web [rootiZ2ze66tzux2otcpbvie88Z ~]# ls web [rootiZ2ze66tzux2otcpbvie88Z ~]# 创建一个文件2.1 在 Li…...

基于STM32设计的音乐播放器

一、项目背景与设计思路 1.1 项目背景 时代进步,科学技术的不断创新,促进电子产品的不断更迭换代,各种新功能和新技术的电子产品牵引着消费者的眼球。人们生活水平的逐渐提高,对娱乐消费市场需求日益扩大,而其消费电子产品在市场中的占有份额越来越举足轻重。目前消费电…...

微服务开发

目录 微服务配置管理 权限认证 批处理 定时任务 异步 微服务调用 (协议)...

【(C语言)数据结构奋斗100天】二叉树(上)

【(C语言)数据结构奋斗100天】二叉树&#xff08;上&#xff09; &#x1f3e0;个人主页&#xff1a;泡泡牛奶 &#x1f335;系列专栏&#xff1a;数据结构奋斗100天 本期所介绍的是二叉树&#xff0c;那么什么是二叉树呢&#xff1f;在知道答案之前&#xff0c;请大家思考一下…...

Java 验证二叉搜索树

验证二叉搜索树中等给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。有效 二叉搜索树定义如下&#xff1a;节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。示例 1&…...

HarnessGate:专为AI Agent设计的纯消息网关,实现多平台无缝桥接

1. 项目概述&#xff1a;一个纯粹的AI Agent消息网关如果你正在构建一个需要对接多个聊天平台&#xff08;比如Telegram、Discord、Slack&#xff09;的AI助手或客服机器人&#xff0c;你很可能已经踩过这样的坑&#xff1a;市面上主流的机器人框架&#xff0c;比如Botpress、L…...

AI信息摘要工具:从数据采集到智能推送的完整实践指南

1. 项目概述&#xff1a;一个AI驱动的每日信息摘要工具最近在GitHub上看到一个挺有意思的项目&#xff0c;叫“ai-daily-digest”。光看名字&#xff0c;你大概能猜到它的核心功能&#xff1a;利用人工智能技术&#xff0c;自动为你生成每日的信息摘要。作为一个经常被信息洪流…...

2026年强烈建议收藏:八大热门AI编程工具权威评测

AI编程工具已全面进入智能体时代&#xff0c;从单一代码补全进化为全流程开发引擎。本文精选8款全球主流工具&#xff0c;从核心能力、场景适配、使用体验等维度客观解析&#xff0c;为开发者提供精准选型参考。 一、Trae&#xff08;字节跳动旗下&#xff09;—— 全链路AI原生…...

Sora 2如何“唤醒”3D Gaussian Splatting?:从神经辐射场到毫秒级动态场景生成的4层技术跃迁解析

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;Sora 2与3D Gaussian Splatting融合的范式革命 传统视频生成模型受限于体素网格或NeRF隐式表示的计算开销与几何保真度瓶颈&#xff0c;而Sora 2通过引入时空一致性token压缩机制&#xff0c;与3D Gaus…...

为AI智能体构建可编程邮箱:mailbot实战指南

1. 项目概述&#xff1a;为AI智能体打造专属的“可编程邮箱”如果你正在开发一个AI智能体&#xff0c;无论是客服机器人、自动化工作流还是个人助理&#xff0c;让它具备收发邮件的能力往往是刚需。传统的做法是什么&#xff1f;要么去折腾Gmail的API&#xff0c;忍受OAuth授权…...

微信自动化终极指南:5个强大功能助你高效管理微信数据

微信自动化终极指南&#xff1a;5个强大功能助你高效管理微信数据 【免费下载链接】wechat-toolbox WeChat toolbox&#xff08;微信工具箱&#xff09; 项目地址: https://gitcode.com/gh_mirrors/we/wechat-toolbox 还在为繁琐的微信数据管理而烦恼吗&#xff1f;微信…...

Flutter + 开源鸿蒙实战 | 极简记账本 Day1:项目初始化 + 底部导航框架搭建

&#x1f525; Flutter 开源鸿蒙实战 | 极简记账本 Day1&#xff1a;项目初始化 底部导航框架搭建欢迎加入开源鸿蒙跨平台社区&#xff1a;https://openharmonycrossplatform.csdn.net 系列项目&#xff1a;极简记账本&#xff08;6 天完结&#xff09;环境&#xff1a;Flutt…...

使用Nodejs和Taotoken为前端应用构建AI聊天后端

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 使用Node.js和Taotoken为前端应用构建AI聊天后端 基础教程类&#xff0c;指导前端或全栈开发者使用Node.js环境接入Taotoken&#…...

2026 AI大模型API加速网站推荐

在AI开发领域&#xff0c;一个现实问题始终困扰着开发者&#xff1a;如何接入模型厂商的官方API&#xff1f;在海外&#xff0c;注册、绑卡、调用这三个步骤就能轻松解决。然而&#xff0c;国内开发者面临着跨境网络波动、外币支付门槛、发票合规需求以及多厂商Key碎片化管理等…...

Windows 11优化终极指南:使用Win11Debloat一键提升电脑性能51%

Windows 11优化终极指南&#xff1a;使用Win11Debloat一键提升电脑性能51% 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutte…...