C++11 设计模式5. 原型模式

什么是原型模式?

        原型模式⼀种创建型设计模式,该模式的核⼼思想是基于现有的对象创建新的对象,⽽不是从头开始创建。在原型模式中,通常有⼀个原型对象,它被⽤作创建新对象的模板。新对象通过复制原型对象的属性和状态来创建,⽽⽆需知道具体的创建细节。

Prototype模式说简单点,就是提供了一个clone, 通过已存在对象进行新对象创建。clone()实现和具体的实现语言相关,在C++中我们通过拷贝构造函数实现。

那为啥要写clone的接口来实现这个目的呢?直接使用拷贝构造不香么,知乎中看到陈硕大佬对此的一个回答,觉得豁然开朗。

Prototype 的意义在于,你拿到一个 基类指针 Base* ,
它指向某个 派生类 Derived 对象,
你想克隆出 Derived对象,但代码中不写出 Derived 的具体类型,
因为有很多派生类,这种情况下你用构造函数是搞不定的。
switch-case 是 bad smells 。
另外,这里考虑 virtual 的性能损失是主次不分,
构造对象需要分配内存,这开销比一次虚函数调用大多了。
–陈硕在知乎中的回答

复习拷贝构造函数实现

在学习原型模式之前,我们先来看一下C++中深拷贝和先拷贝问题

UML 

两种角色:

1. prototype(抽象原型类) Monster类

2. ConcretePrototype 具体原型类:在clone方法中 return一个自己的对象

        M_undead,M_Element,M_Mechanic

那么什么时候才能使用这个原型对象呢?

我们以 魔兽争霸 这款游戏中的 分身系英雄为例,当 剑圣 出镜像的时候,5狗齐飞,分身斧,这些道具或者技能使用的时候,分裂出来的这些英雄都应该有当前英雄的这些属性,然后将属性微调就可以,例如,分身的攻击力只有原先的30%,受到伤害是原先的150%。就可以用 原型模型。

这里的核心是 魔兽争霸的英雄 属性太多了,还有6个框框的物品栏,分裂的瞬间,都要将这些属性拷贝过去。--也就是说:

如果对象的内部数据比较复杂多变,并且在创建对象的时候希望爆出对象的当前状态,那么用原型模式显然比用工厂方法模式更合适

工厂方法模式和原型模式在创建对象时的异同点

a。都不需要程序员知道所创建对象所属的类名

b.工厂方法模式中的createMonster仍旧属于根据类名来生成新对象

c 原型模式clone是根据现有对象来生成新对象,会调用 copy构造方法 。 

d 原型模型不像工厂模式,不需要额外的等级结构创建多个工厂类 

原型模式的优缺点:

如果创建新对象的背部数据比较复杂且多变,原型模式创建对象的效率可能高的多

需要在clone中完成对象的拷贝,特别是深拷贝和浅拷贝问题,但是一般都调用 copy构造方法

当前copy构造函数中也可以调用 operator= 的重写。

这样也可以使用 "= " 完成拷贝

原型模式和直接 M_undead(m_undead1) 有啥区别?

既然原型模式的 也是调用  copy构造函数,那么直接用 如下的代码不就行了吗?为啥还弄个原型模式?

M_undead m_undead2 = M_undead(m_undead1)

1. C++才有 copy构造,java ,C#并没有,设计模式是独立于变成 语言存在的,因此原型模式的存在是有意义的。

2.你拿到一个 基类指针 Base* ,
它指向某个 派生类 Derived 对象,
你想克隆出 Derived对象,但代码中不写出 Derived 的具体类型,
因为有很多派生类,这种情况下你用构造函数是搞不定的。
switch-case 是 bad smells 。

代码

在代码中还复习了 移动构造函数,和 移动operator=运算符的重写。

// 原型模型//原型模式⼀种创建型设计模式,
//该模式的核⼼思想是基于现有的对象创建新的对象,
//⽽不是从头开始创建。在原型模式中,通常有⼀个原型对象,
//它被⽤作创建新对象的模板。
//新对象通过复制原型对象的属性和状态来创建,⽽⽆需知道具体的创建细节。
//
//Prototype模式说简单点,就是提供了一个clone, 
//通过已存在对象进行新对象创建。
//clone()实现和具体的实现语言相关,
//在C++中我们通过拷贝构造函数实现。
//
//那为啥要写clone的接口来实现这个目的呢?
//直接使用拷贝构造不香么?
//
//Prototype 的意义在于,你拿到一个 基类指针 Base* ,
//它指向某个 派生类 Derived 对象,
//你想克隆出 Derived对象,但代码中不写出 Derived 的具体类型,
//因为有很多派生类,这种情况下你用构造函数是搞不定的。
//switch - case 是 bad smells 。
//另外,这里考虑 virtual 的性能损失是主次不分,
//构造对象需要分配内存,这开销比一次虚函数调用大多了。//
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include "stdlib.h"
#include "stdio.h"
#include "stdint.h"
using namespace std;//在学习原型模式之前,需要先复习一下 C++ 的 深拷贝浅拷贝问题。
//涉及到的知识点:拷贝构造函数,重写赋值运算符函数,移动构造函数,重写移动赋值运算符函数class Teacher
{
public:Teacher() = default;//=default  表示C++编译器给无参数的构造方法生成函数体//有参数的构造方法Teacher(int age,string sname,char* name,char *othername,char **stuname):_age(age),_sname(sname), _othername(othername),_stuname(stuname){cout << "Teacher 有参数的构造方法被调用" << endl;strcpy(_name, name);}//拷贝构造函数 正常的写法//Teacher(const Teacher& obj) {//	this->_age = obj._age;//	strcpy(_name, obj._name);//	this->_sname = obj._sname;//	//	//如果原先的othername不为nullptr,则先要将原先的othername delete掉。实际代码中//	if (this->_othername!= nullptr) {//		delete this->_othername;//		this->_othername = nullptr;//	}//	if (this->_stuname != nullptr) {//		for (int i = 0; i < 5; i++)//		{//			delete this->_stuname[i];//			this->_stuname[i] = nullptr;//		}//		delete this->_stuname;//		this->_stuname = nullptr;//		//delete [] this->_stuname;//	}//	if (obj._othername != nullptr) {//		int othernamelen = strlen(obj._othername) + 1;//		this->_othername = (char *)malloc(sizeof(char) * othernamelen);//		strcpy(this->_othername,obj._othername);//	}//	if (obj._stuname != nullptr) {//		this->_stuname = (char **)malloc(sizeof(char *) * 5);//		for (int i = 0; i < 5; i++)//		{//			if (obj._stuname[i] != nullptr) {//				int stunamelen = strlen(obj._stuname[i]) + 1;//				this->_stuname[i] = (char *)malloc(sizeof(char *) *stunamelen);//				strcpy(this->_stuname[i],obj._stuname[i]);//			}//		}//	}//}//拷贝构造函数Teacher(const Teacher& obj) {cout << "Teacher 拷贝构造函数被调用" << endl;*this = obj;}//operator = 函数 重写Teacher& operator=( const Teacher & obj) {cout << "Teacher 赋值运算符函数被调用" << endl;this->_age = obj._age;strcpy(_name, obj._name);this->_sname = obj._sname;//如果原先的othername不为nullptr,则先要将原先的othername delete掉。实际代码中if (this->_othername != nullptr) {free( this->_othername);this->_othername = nullptr;}if (this->_stuname != nullptr) {for (int i = 0; i < 5; i++){free( this->_stuname[i]);this->_stuname[i] = nullptr;}free( this->_stuname);this->_stuname = nullptr;//delete [] this->_stuname;}if (obj._othername != nullptr) {int othernamelen = strlen(obj._othername) + 1;this->_othername = (char *)malloc(sizeof(char) * othernamelen);strcpy(this->_othername, obj._othername);}if (obj._stuname != nullptr) {this->_stuname = (char **)malloc(sizeof(char *) * 5);for (int i = 0; i < 5; i++){if (obj._stuname[i] != nullptr) {int stunamelen = strlen(obj._stuname[i]) + 1;this->_stuname[i] = (char *)malloc(sizeof(char *) *stunamelen);strcpy(this->_stuname[i], obj._stuname[i]);}}}return *this;}//移动拷贝函数Teacher( Teacher &&obj) {cout << "Teacher 移动构造函数被调用" << endl;*this = move(obj);}//移动赋值运算符 重写Teacher& operator=(Teacher && obj) {cout << "Teacher 移动赋值运算符 被调用" << endl;this->_age = obj._age;strcpy(_name, obj._name);this->_sname = obj._sname;//如果原先的othername不为nullptr,则先要将原先的othername delete掉。实际代码中if (this->_othername != nullptr) {free( this->_othername);this->_othername = nullptr;}if (this->_stuname != nullptr) {for (int i = 0; i < 5; i++){free( this->_stuname[i]);this->_stuname[i] = nullptr;}free( this->_stuname);this->_stuname = nullptr;//delete [] this->_stuname;}if (obj._othername != nullptr) {int othernamelen = strlen(obj._othername) + 1;this->_othername = (char *)malloc(sizeof(char) * othernamelen);strcpy(this->_othername, obj._othername);}if (obj._stuname != nullptr) {this->_stuname = (char **)malloc(sizeof(char *) * 5);for (int i = 0; i < 5; i++){if (obj._stuname[i] != nullptr) {int stunamelen = strlen(obj._stuname[i]) + 1;this->_stuname[i] = (char *)malloc(sizeof(char *) *stunamelen);strcpy(this->_stuname[i], obj._stuname[i]);}}}//if (obj._othername != nullptr) {free(obj._othername);obj._othername = nullptr;}if (obj._stuname!=nullptr) {for (int i = 0; i < 5; i++){if (obj._stuname[i] !=nullptr) {free( obj._stuname[i]);obj._stuname[i] = nullptr;}}free( obj._stuname);obj._stuname = nullptr;}return *this;}// 写一个clone函数,内部调用 拷贝构造方法,Teacher* clone() {return new Teacher(*this);}~Teacher() {if (this->_othername != nullptr) {free(this->_othername);this->_othername = nullptr;}if (this->_stuname != nullptr) {for (int i = 0; i < 5; i++){free(this->_stuname[i]);this->_stuname[i] = nullptr;}free(this->_stuname);this->_stuname = nullptr;//delete [] this->_stuname;}}
public:int _age = 0 ;string _sname = "";char _name[128] = {0};char *_othername = nullptr;char **_stuname = nullptr;
};
void printTea(Teacher *tea) {if (tea == NULL) {return;}printf("------ printTea start ------\n");printf("tea->age = %d,tea->name = %s,tea->othername=%s\n",tea->_age,tea->_name,tea->_othername);for (size_t j = 0; j < 5; j++){printf("tea->stuname[%d] = %s,  ",j, tea->_stuname[j]);}printf("\n");printf("------ printTea end ------\n");}
int main()
{_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口std::cout << "Hello World!\n";Teacher tea;tea._age = 10;strcpy(tea._name, "tea---name");tea._sname = "teasname";tea._othername = (char *)malloc(sizeof(char) * 128);memset(tea._othername,0,sizeof(char) * 128);strcpy(tea._othername, "teaothername");tea._stuname = (char**)malloc(sizeof(char *) * 5);memset(tea._stuname, 0, sizeof(char *) * 5);for (int i = 0; i < 5; i++){tea._stuname[i] = (char *)malloc(sizeof(char) * 128);memset(tea._stuname[i], 0, sizeof(char) * 128);sprintf(tea._stuname[i], "teastuname%d", i + 100);}printTea(&tea);cout << "---------" << endl;Teacher tea1 = tea;printTea(&tea1);cout << "----1111111111-----" << endl;Teacher tea2 ;tea2 = tea;printTea(&tea2);cout << "----2222222222-----" << endl;Teacher tea3 = move(tea);printTea(&tea3);cout << "----333333333-----" << endl;Teacher tea4;tea4 = move(tea3);printTea(&tea4);// 原型模式Teacher.clone方法的调用Teacher *tea5 = tea4.clone();printTea(tea5);delete tea5;//这里有些Teacher 的 析构函数,因此delete 会调用析构函数,
}

继承中的代码

namespace _nmsp2
{//怪物父类class Monster{public://构造函数Monster(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack) {}virtual ~Monster() {} //做父类时虚构函数应该为虚函数public:virtual Monster* clone() = 0; //具体的实现在子类中进行//public://	void setlife(int tmplife)//	{//		m_life = tmplife;//	}protected: //可能被子类访问的成员,所以用protected修饰//public://怪物属性int m_life;  //生命值int m_magic; //魔法值int m_attack;  //攻击力};//亡灵类怪物class M_Undead :public Monster{public://构造函数M_Undead(int life, int magic, int attack) :Monster(life, magic, attack){cout << "一只亡灵类怪物来到了这个世界" << endl;}//拷贝构造函数//..........留给大家自己写virtual Monster* clone(){//return new M_Undead(300, 50, 80); //创建亡灵类怪物return new M_Undead(*this); //触发拷贝构造函数的调用来创建亡灵类怪物/*Monster * pmonster = new M_Undead(300, 50, 80); //创建亡灵类怪物//pmonster->m_life = m_life;pmonster->setlife(m_life);pmonster->m_magic = m_magic;pmonster->m_attack = m_attack;return pmonster;*/}//...其他代码略};//元素类怪物class M_Element :public Monster{public://构造函数M_Element(int life, int magic, int attack) :Monster(life, magic, attack){cout << "一只元素类怪物来到了这个世界" << endl;}//拷贝构造函数M_Element(const M_Element& tmpobj) :Monster(tmpobj) //初始化列表中注意对父类子对象的初始化{cout << "调用了M_Element::M_Element(const M_Element& tmpobj)拷贝构造函数创建了一只元素类怪物" << endl;}virtual Monster* clone(){//return new M_Element(200, 80, 100); //创建元素类怪物return new M_Element(*this);}//...其他代码略};//机械类怪物class M_Mechanic :public Monster{public://构造函数M_Mechanic(int life, int magic, int attack) :Monster(life, magic, attack){cout << "一只机械类怪物来到了这个世界" << endl;}//拷贝构造函数M_Mechanic(const M_Mechanic& tmpobj) :Monster(tmpobj) //初始化列表中注意对父类子对象的初始化{cout << "调用了M_Mechanic::M_Mechanic(const M_Mechanic& tmpobj)拷贝构造函数创建了一只机械类怪物" << endl;}virtual Monster* clone(){//return new M_Mechanic(400, 0, 110); //创建机械类怪物return new M_Mechanic(*this);}//...其他代码略};//全局的用于创建怪物对象的函数/*void Gbl_CreateMonster2(Monster* pMonster){Monster* ptmpobj = nullptr;if (dynamic_cast<M_Undead*>(pMonster) != nullptr){ptmpobj = new M_Undead(300, 50, 80); //创建亡灵类怪物}else if (dynamic_cast<M_Element*>(pMonster) != nullptr){ptmpobj = new M_Element(200,80, 100); //创建元素类怪物}else if (dynamic_cast<M_Mechanic*>(pMonster) != nullptr){ptmpobj = new M_Mechanic(400, 0, 110); //创建机械类怪物}if (ptmpobj != nullptr){//这里就可以针对ptmpobj对象实现各种业务逻辑//......//不要忘记释放资源delete ptmpobj;}}*/void Gbl_CreateMonster2(Monster* pMonster){Monster* ptmpobj = pMonster->clone(); //根据已有对象直接创建新对象,不需要知道已有对象所属的类型//这里就可以针对ptmpobj对象实现各种业务逻辑//......//不要忘记释放资源delete ptmpobj;}
}

调用

	_nmsp2::M_Mechanic myPropMecMonster(400, 0, 110); //创建一只机械类怪物对象作为原型对象以用于克隆目的_nmsp2::Monster* pmyPropEleMonster = new _nmsp2::M_Element(200, 80, 100); //创建一只元素类怪物对象作为原型对象以用于克隆目的,//这里可以直接new,也可以通过工厂模式创建原型对象,取决于程序员自己的洗好。//....._nmsp2::Monster* p_CloneObj1 = myPropMecMonster.clone(); //使用原型对象克隆出新的机械类怪物对象_nmsp2::Monster* p_CloneObj2 = pmyPropEleMonster->clone(); //使用原型对象克隆出新的元素类怪物对象_nmsp2::Monster* p_CloneObj3 = new _nmsp2::M_Mechanic(myPropMecMonster);//可以对p_CloneObj1、p_CloneObj2所指向的对象进行各种操作(实现具体的业务逻辑)//......//释放资源//释放克隆出来的怪物对象delete p_CloneObj1;delete p_CloneObj2;//释放原型对象(堆中的)delete pmyPropEleMonster;_nmsp2::Monster* pMonsterObj = new _nmsp2::M_Element(200, 80, 100);_nmsp2::Gbl_CreateMonster2(pMonsterObj);delete pMonsterObj;

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp 之 开发微信小程序入门详细指南

目录 配置运行设置&#xff08;编辑器的设置&#xff09;项目目录文件配置基础配置中的uniapp应用标识&#xff08;AppID&#xff09;配置微信小程序的AppID 总结 配置运行设置&#xff08;编辑器的设置&#xff09; 点击编辑器上方菜单栏 - 运行 - 运行到小程序模拟器 - 运行…

面试经典150题——求根节点到叶节点数字之和

​ 1. 题目描述 2. 题目分析与解析 2.1 思路一——DFS 理解问题&#xff1a; 首先要理解题目的要求&#xff0c;即对于给定的二叉树&#xff0c;我们需要找出从根节点到所有叶子节点的所有路径&#xff0c;然后将每一条路径上的数字组成一个整数&#xff0c;最后求出这些整数…

ios微信小程序禁用下拉上拉

第一步&#xff1a; page.json配置页面的"navigationStyle":"custom"属性&#xff0c;禁止页面滑动 "navigationStyle":"custom" 第二步&#xff1a; 页面里面使用scroll-view包裹内容&#xff0c;内容可以内部滑动 <view class&…

AI赋能不应贵气:深度解读AI助力企业渡过经济寒冬以及如何落地AI的路径

AI很棒可是给人感觉“很贵”因此我不敢用 继GPT4后Dalle3、Sora、GPT4.5、GPT5的消息以及前天突然出现的GPT 2.0&#xff08;GPT二代&#xff0c;有人说这就是OPEN AI的新产品&#xff1a;Q*&#xff09;但凡涉及到AI的一系列新闻给人予很震撼的感觉。放眼望去AI正在欣欣向荣。…

【bug已解决】发生错误,导致虚拟 CPU 进入关闭状态。如果虚拟机外部发生此错误,则可能已导致物理计算机重新启动......

本bug报错已找到原因,并成功解决。 项目场景: vmware安装ubuntu报错。 如下: 发生错误,导致虚拟 CPU 进入关闭状态。如果虚拟机外部发生此错误,则可能已导致物理计算机重新启动。错误配置虚拟机、客户机操作系统中的错误或 VMware Workstation 中的问题都可以导致关闭状…

uniapp微信小程序开发踩坑日记:由于图表数据渲染不出来,我第一次在项目中用watch函数监听数据变化

一、发现问题 在我们团队自己开发的微信小程序中&#xff0c;引入了Echarts图表库 然后突然有一天&#xff0c;后端队友反应图表渲染有问题。后面我去试了一下&#xff0c;确实20次里面必有一次数据渲染不出来 断定代码没问题&#xff0c;于是我们将其鉴定为玄学 二、问题原因…

GPU 架构与 CUDA 关系 并行计算平台和编程模型 CUDA 线程层次结构 GPU 的算力是如何计算的 算力峰值

GPU 架构与 CUDA 关系 本文主要包含 NVIDIA GPU 硬件的基础概念、CUDA(Compute Unified Device Architecture)并行计算平台和编程模型,详细讲解 CUDA 线程层次结构,最后将讲解 GPU 的算力是如何计算的,这将有助于计算大模型的算力峰值和算力利用率。 GPU 硬件基础概念GP…

ClickHouse安装(成功安装)

1.下载安装包 下面通过阿里镜像&#xff08;https://mirrors.aliyun.com/clickhouse/rpm/lts/&#xff09;进行下载&#xff0c;下载哪里&#xff0c;自行指定。 # deb包下载使用如下4行 wget https://mirrors.aliyun.com/clickhouse/deb/pool/stable/clickhouse-client_22.8…

AnomalyGPT——使用大型视觉语言模型进行工业异常检测的算法解析与应用

1.概述 工业缺陷检测是工业自动化和质量控制中的一个重要环节&#xff0c;其目的是在生产过程中识别和分类产品或组件中的缺陷&#xff0c;以确保最终产品的质量满足既定标准。这项技术的应用可以显著提高生产效率&#xff0c;降低成本&#xff0c;并减少由于缺陷产品导致的潜…

网络原理(qq消息发送原理)

1.网络初识 IP地址 概念&#xff1a; IP地址主要⽤于标识⽹络主机、其他⽹络设备&#xff08;如路由器&#xff09;的⽹络地址。简单说&#xff0c;IP地址⽤于定位主机的⽹络地址。 就像我们发送快递⼀样&#xff0c;需要知道对⽅的收货地址&#xff0c;快递员才能将包裹送到…

智能家居—ESP32开发环境搭建

相关文章 毕业设计——基于ESP32的智能家居系统(语音识别、APP控制) 智能家居—ESP32开发环境搭建 一、下载安装二、验证三、资料获取 一、下载安装 下载安装 vscode 安装插件 创建工程 二、验证 写一个简单的函数来验证一下功能 void setup() {// put your setup c…

进一步了解android studio 里 AGP,gradle等关系

目录 &#xff08;1&#xff09; gradle是什么 &#xff08;2&#xff09; 工程的jdk版本&#xff0c;及引用包的编译版本的关系 实践 问题与解决 编译成功与运行成功 编译成功 运行成功 &#xff08;1&#xff09; gradle是什么 Gradle是一个构建工具&#xff0c;它是…

Django后台项目开发实战一

开发环境使用 Anaconda, IDE 使用 pycharm 第一阶段 创建 Django 项目 在 Anaconda Prompt 中逐步输入下面的命令&#xff08;之后的所有命令都在这个&#xff09; 首先创建一个虚拟环境&#xff0c;名称自拟&#xff0c;python 版本我这里使用 3.9.18 关于 python 版本和…

RuoYi-Vue-Plus (SPEL 表达式)

RuoYi-Vue-Plus 中SPEL使用 DataScopeType 枚举类中&#xff1a; /*** 部门数据权限*/DEPT("3", " #{#deptName} #{#user.deptId} ", " 1 0 "), PlusDataPermissionHandler 拦截器中定义了解析器&#xff1a; buildDataFilter 方法中根据注解的…

Swift - 流程控制

文章目录 Swift - 流程控制if-else2. while3. for3.1 闭区间运算符3.2 半开区间运算符3.3 for - 区间运算符用在数组上3.3.1 单侧区间 3.4 区间类型3.5 带间隔的区间值 4. switch4.1 fallthrough4.2 switch注意点 5. 复合条件6. 区间匹配、元组匹配7. 值绑定8. where9. 标签语句…

【C语言】编译与链接

1.翻译环境与运行环境 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。 1.翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令&#xff08;二进制指令&#xff09; 2.执行环境&#xff0c;它用于实际执行代码 2.翻译环境 那么翻译环境是怎么将源代码…

Java:七大基于比较的排序算法——上(思想+代码实现 超详细!)

冒泡排序、堆排序、插入排序、归并排序、快速排序、选择排序、希尔排序 目录 一、冒泡排序 1、基本思想 2、特征总结 3、代码实现 二、堆排序 1、基本思想 2、特征总结 3、代码实现 三、插入排序 1、基本思想 2、特征总结 3、代码实现 四、选择排序 1、基本思想 …

带宽的理解-笔记

带宽的理解 带宽(频带宽度)&#xff1a;是指电磁波最高频率和最低频率的差值&#xff0c;这一段频率被称为带宽。 举例说明 人耳能听到的频率范围是20赫兹到2万赫兹。换句话说&#xff0c;人而只对20赫兹至2万赫兹的声音频率有反应&#xff0c;超出或低于这一频率范围的声音我…

上市企业数字赋能指数数据集-2001到2022年(TF-IDF)

01、数据简介 上市公司数字赋能指数是一个用来衡量上市公司利用数字技术提高业务能力和效率的指标。这个指数反映了上市公司利用大数据、云计算和人工智能等数字技术&#xff0c;高效地利用商业资源和信息&#xff0c;并扩展供应关系的能力。市公司数字赋能指数是一种综合性的…

小米汽车充电枪继电器信号

继电器型号&#xff1a; 参考链接 小米SU7&#xff0c;便捷充放电枪拆解 (qq.com)https://mp.weixin.qq.com/s?__bizMzU5ODA2NDg4OQ&mid2247486086&idx1&sn0dd4e7c9f7c72d10ea1c9f506faabfcc&chksmfe48a110c93f2806f6e000f6dc6b67569f6e504220bec14654ccce7d…