模拟实现string【C++】

文章目录

  • 全部的实现代码放在了文章末尾
  • 准备工作
    • 包含头文件
    • 定义命名空间和类
      • 类的成员变量
  • 构造函数
    • 默认构造
    • 拷贝构造
  • 重载赋值拷贝函数
  • 析构函数
  • 迭代器和获取迭代器
    • 迭代器
    • 获取迭代器
  • resize【调整size】
    • 图解
  • reserve【调整capacity】
  • empty【判断串是否为空】
  • operator[]
  • append
  • push_back
  • operator+=
  • insert【在pos之前插入字符串】
  • erase【删除从pos开始的len个字符】
  • swap
  • find
  • substr
  • operator+
  • compare
  • 比较运算符重载
    • operator>
    • operator==
    • operator<
    • operator>
    • operator<=
    • operator!=
  • c_str
  • operator<<【输出运算符重载】
  • operator>>【输入运算符重载】
  • 全部代码
    • mystring.h
    • mystring.cpp

全部的实现代码放在了文章末尾

准备工作

创建三个文件,一个头文件mystring.h,两个源文件mystring.cpp tesr.cpp
因为是简单实现,所以只实现了string里放char这一种实现,就没有用模板了

  1. mystring.h:存放包含的头文件,命名空间的定义
  2. mystring.cpp:存放成员函数和命名空间中的函数的定义
  3. test.cpp:存放main函数,以及测试代码

包含头文件

  1. iostream:用于输入输出
  2. string.h:C语言的头文件,用于使用其中的操作字符数组的函数
  3. assert.h:用于使用报错函数

定义命名空间和类

在文件mystring.hmystring.cpp中都定义上一个命名空间mystring
mystring.h中类的声明放进命名空间,把mystring中的函数实现也放进命名空间

注意:
不同源文件的同名的命名空间经过编译链接之后可以合成在一起

类的成员变量

在这里插入图片描述


构造函数

默认构造

在这里插入图片描述

给默认构造的str加上缺省值(“”)就可以实现不传参数时就是空串了


拷贝构造

因为成员str申请了堆区空间,所以要手写拷贝构造,并且要实现深拷贝
在这里插入图片描述


重载赋值拷贝函数

因为成员str申请了堆区空间,所以要手写赋值拷贝,并且要实现深拷贝
在这里插入图片描述
为什么要防止自己给自己赋值?
【this指针指向接收赋值的对象,所以只要this传入的参数的地址相等就是自己赋值给自己】

因为要深拷贝,所以要把一个对象中的所有成员都拷贝一遍,时间复杂度是
O(N),所以有必要防止自己给自己赋值


析构函数

因为成员str申请了堆区空间,所以要手写析构,不能用编译器给的默认析构

在这里插入图片描述


迭代器和获取迭代器

迭代器

因为存放字符的空间的地址是连续的,且只是简单模拟
所以我用了char*作为普通迭代器,const char*作为const迭代器

直接把char*重命名为iterator,把const char*重命名为const_iterator就完成了迭代器的实现


获取迭代器

在这里插入图片描述
因为使用了char*作为迭代器
所以str就是第一个有效元素,str+size就是最后一个有效字符的下一个位置(\0)

又因为const修饰的对象只能调用const修饰的成员函数
所以如果是const修饰的对象调用begin()和end()的时候,就会自动调用到const修饰的begin和end.
在这里插入图片描述


resize【调整size】

void string::resize(size_t n,char c='\0')
{if (n > _capacity) 当要调整的容量n大于最大容量时,就要扩容{char* tmp = new char[n +1];申请size+1个空间,那多出来的1是给'\0'strcpy(tmp, _str);拷贝字符串(把右参数拷贝给左参数)delete[] _str;释放扩容前str指向的空间_str = tmp;完成扩容for (; _size <n; _size++)把多出来的有效字符用c补上{_str[_size] = c;}_str[_size] = '\0'; 补完之后加上字符串结束标志'\0'_capacity = n;更新最大容量}else{if (n > _size)如果调整之后的size  大于  原来的size就要把少(n-size)的有效字符用c补上{for (; _size < n; _size++)把多出来的有效字符用c补上{_str[_size] = c;}_str[_size] = '\0';补完之后加上字符串结束标志'\0'}else 如果调整之后的size  小于  原来的size就要把多的字符删除{_size = n;调整有效字符大小_str[_size] = '\0';直接把n位置改成'\0'即可删除多余字符}}
}

图解

在这里插入图片描述

在这里插入图片描述


reserve【调整capacity】

在这里插入图片描述


empty【判断串是否为空】

在这里插入图片描述


operator[]

返回值要是char&这样才能像字符数组一样访问和修改串中的字符
在这里插入图片描述


append

在这里插入图片描述

在这里插入图片描述


push_back

可以直接服用append
在这里插入图片描述


operator+=

也可以直接服用append

在这里插入图片描述


insert【在pos之前插入字符串】

在这里插入图片描述


erase【删除从pos开始的len个字符】

在这里插入图片描述


swap

因为存放字符串的空间是在堆区开辟的,用成员str去指向的
所以直接交换两个对象的str中存放的地址就可以完成存储的字符串的交换了

不需要拷贝
在这里插入图片描述

在这里插入图片描述


find

在这里插入图片描述


substr

在这里插入图片描述


operator+

直接复用operator+=
因为+不改变字符串本身,所以要用一个临时对象存储+=之后的字符,再用传值返回即可

在这里插入图片描述


compare

在这里插入图片描述


比较运算符重载

operator>

复用compare
在这里插入图片描述


operator==

复用compare
在这里插入图片描述


operator<

复用operator>operator==
在这里插入图片描述


operator>

复用operator>operator==
在这里插入图片描述


operator<=

复用operator>

在这里插入图片描述


operator!=

复用operator==
在这里插入图片描述


c_str

作用是获取string对象中的str成员。

一般是要在C++中使用C语言函数时使用,因为C语言不支持string,所以只能用字符指针代替一下

在这里插入图片描述

operator<<【输出运算符重载】

重载了之后就可以
直接用cout输出string实例化的对象了

string a(“aaaaa”);
cout<<a<<endl;

在这里插入图片描述


operator>>【输入运算符重载】

重载了之后就可以
直接用cin把字符串输入到string实例化的对象里面了

istream是输入流对象,比如我们常用的  cin  就是  istream实例化的对象istream& operator>>(istream& is, string& obj)
{char str[100];  存储  输入的  每一行的字符int sum = 0;  使用sum记录输入的  每一行  的字符个数char c = 0;\n是换行符(回车),所以  没遇到  \n就  没换行while ((c = is.get()) != '\n')  一次读取一个字符{if (sum == 99)  当sum等于99时说明  str数组存不下了{str[sum] = '\0';  末尾加上了\0才是字符串obj += str;  使用+=把str数组中的字符先加上去sum = 0;  sum置成0,继续记录}else  否则{把读取到的字符存进str数组里str[sum++] = c;}}sum!=0说明最后输入的最后一行字符个数  小于99个,没有+=if (sum != 0){str[sum] = '\0';  末尾加上了\0才是字符串obj += str;  使用+=把str数组中的字符先加上去}return is;  为支持链式编程,返回  输入流对象  的引用
}

全部代码

mystring.h

#include<iostream>
#include<string.h>
#include<assert.h>
using namespace std;namespace mystring
{class string{public:typedef char* iterator;typedef const char* const_iterator;string(const string& obj);string(const char* str = "");string& operator=(const string&obj);~string();iterator begin();const_iterator begin() const;iterator end();const_iterator end() const;size_t size() const;size_t capacity() const;void resize(size_t n, char c='\0');void reserve(size_t n = 0);void clear();bool empty() const;char& operator[](size_t pos);const char& operator[](size_t pos)const;string& append(const string& obj);string& append(char ch);void push_back(char c);string& operator+=(const string& obj);string& operator+=(char c);string& assign(const string& str);string& insert(size_t pos, const string& str);string& insert(size_t pos,char c);string& erase(size_t pos = 0, size_t len = npos);void swap(string& str);const char* c_str() const;size_t find(const string& str, size_t pos = 0) const;size_t find(char c, size_t pos = 0) const;string substr(size_t pos = 0, size_t len = npos) const;int compare(const string& str) const;string operator+(const string& obj)const;string operator+(char c)const;bool operator>(const string&obj)const;bool operator<(const string& obj)const;bool operator>=(const string& obj)const;bool operator<=(const string& obj)const;bool operator==(const string& obj)const;bool operator!=(const string& obj)const;private:char* _str;//指向存放字符串的堆区空间的指针size_t _size;//字符串的有效字符个数size_t _capacity;//字符串的最大容量static const size_t npos;//理论上可以存储的最大字符个数};ostream& operator<< (ostream& os, const string& obj);istream& operator>>(istream& is,string& obj);
}

mystring.cpp

#include"string.h"namespace mystring
{const size_t string::npos = -1;string::string(const string& obj)//因为成员在堆区申请了空间所以要写深拷贝的拷贝构造{_str = new char[obj._size + 1];// 申请size + 1个空间,那多出来的1是给'\0'的strcpy(_str, obj._str);//使用该函数把字符串把obj中的字符串拷贝过来_capacity = obj._capacity;_size = obj._size;}string::string(const char* str) //让str的缺省值为""(空字符串):_size(strlen(str))//构造函数会先走成员初始化列表,借此先计算出size{assert(str != nullptr);//防止传入的字符指针是空的_str = new char[_size + 1];//申请size+1个空间,那多出来的1是给'\0'的strcpy(_str, str);//使用该函数把字符串str中的字符拷贝过来_capacity = _size;//设置初始最大容量和size一样大}string& string::operator=(const string&obj){if (this != &obj)//防止自己赋值给自己{delete[] _str;//先释放接收赋值之前str指向的堆区空间//因为接收赋值之后,str中存放的地址就被覆盖了_str = new char[obj._size + 1];//申请size+1个空间,那多出来的1是给'\0'的strcpy(_str, obj._str);//拷贝字符串(把右参数拷贝给左参数)_size = obj._size;_capacity = obj._capacity;}return *this;}string::~string(){delete[] _str;//释放str指向的堆区空间_str = nullptr;_size = 0;_capacity = 0;}string::iterator string::begin()//普通起始迭代器{return _str;}string::const_iterator string::begin() const//const起始迭代器{return _str;}string::iterator  string::end()//普通结束迭代器{return _str + _size;}string::const_iterator  string::end() const//const结束迭代器{    return _str + _size;}size_t  string::size() const{return _size;}void string::resize(size_t n,char c){if (n > _capacity)//当要调整的size,n大于最大容量时,就要扩容{char* tmp = new char[n +1];//申请size+1个空间,那多出来的1是给'\0'的strcpy(tmp, _str);//拷贝字符串(把右参数拷贝给左参数)delete[] _str;//释放扩容前str指向的空间_str = tmp;//完成扩容for (; _size <n; _size++)//把多出来的有效字符用c补上{_str[_size] = c;}_str[_size] = '\0'; //补完之后加上字符串结束标志'\0'_capacity = n;//更新最大容量}else{if (n > _size)//如果调整之后的size  大于  原来的size//就要把少(n-size)的有效字符用c补上{for (; _size < n; _size++)//把多出来的有效字符用c补上{_str[_size] = c;}_str[_size] = '\0';//补完之后加上字符串结束标志'\0'}else//如果调整之后的size  小于  原来的size//就要把多的字符删除{_size = n;//调整有效字符大小_str[_size] = '\0';//直接把n位置改成'\0'即可删除多余字符}}}size_t string::capacity() const{return _capacity;}void string::reserve(size_t n)//注意:n是指元素个数,不是字节数{if (n > _capacity)//当要调整的容量n大于capacity时,才扩容{char* tmp = new char[n + 1];//申请size+1个空间,那多出来的1是给'\0'的strcpy(tmp, _str);//拷贝字符串(把右参数拷贝给左参数)delete[] _str;//拷贝之后再释放旧空间_str = tmp;//指向新开辟的空间_capacity = n;//更改最大容量}}void string::clear(){_size = 0;_str[0] = '\0';}bool string::empty() const{return _size == 0;//如果size==0就为真,就会return true//如果size!=0就为假,就会return false}char& string::operator[](size_t pos){assert(pos<_size);//防止越界访问return _str[pos];//因为存放字符的空间是连续的//所以直接像数组一样,使用pos位置的字符就可以}//const修饰的对象会自动调用下面这个const char& string::operator[](size_t pos)const{assert(pos < _size);//防止越界访问return _str[pos];}//在串尾加上一个string对象或者  字符串【可以隐式类型转换为string对象】string& string::append(const string& obj){if (_capacity < obj._size + _size)//如果容量不够了{reserve(obj._size + _size);//就扩容}//从指定地址开始  拷贝字符串(把右参数拷贝给左参数)strcpy(_str + _size, obj._str);_size += obj._size;//改变有效字符个数return *this;}//在串尾加上一个字符string& string::append(char ch){if (_capacity <_size+1)//如果容量不够了{reserve(_size + 1);//就扩容}_str[_size++] = ch;_str[_size] = '\0';//\0被ch覆盖了,再加回来return *this;}void string::push_back(char c){append(c);//复用append,尾插一个字符c}string& string::operator+=(const string& obj){append(obj);//复用append,尾插一个string对象/字符串return *this;}string& string::operator+=(char c){append(c);//复用append,尾插一个字符creturn *this;}string& string::assign(const string& obj){*this = obj;return *this;}string& string::insert(size_t pos, const string& obj){if (_capacity < obj._size + _size)//如果容量不够了{reserve(obj._size + _size);//就扩容}//从要插入的pos位置开始,把pos和其之后的字符都//向后移动  传入的对象的size个位置for (int i = _size; i >=(int) pos; i--){_str[i + obj._size] = _str[i];}//把字符串插入进去for (int i = pos,j=0; i<obj._size+pos; i++,j++){_str[i] = obj._str[j];}_size += obj._size;_str[_size] = '\0';//再补上\0return *this;}string& string::insert(size_t pos, char c){if (_capacity < _size + 1){reserve(_size + 1);}for (int i = _size; i >=(int) pos; i--){_str[i + 1] = _str[i];}_str[pos] = c;return *this;}string& string:: erase(size_t pos , size_t len){if (len > _size - pos)//如果len大于pos及其之后的字符的长度  或者 len==npos{_str[pos] = '\0';//就直接把pos及其之后的字符  全部删除_size = pos;//把size更改成新的有效长度}else//否则{//把从pos开始的字符都  用它+len之后的字符进行覆盖,即可完成删除for (int i = pos; i <=_size-len; i++){_str[i] = _str[i + len];}_size -= len;//把size更改成新的有效长度}return *this;}void string::swap(string& obj){//使用库里面的swap把 两个对象的成员变量交换即可std::swap(_str, obj._str);std::swap(_size, obj._size);std::swap(_capacity, obj._capacity);}//获取string对象中的  str成员const char* string::c_str() const{return _str;}//从pos位置开始查找字符串size_t string::find(const string&obj, size_t pos) const{const char* p = NULL; //p为找到的字符串的首地址//使用string.h里面的strstr查找子串,如果  找不到  就返回NULLif ((p=strstr(_str + pos, obj._str)) == NULL){return npos;//找不到  就返回  npos}else{return p - _str;//返回下标,p为找到的字符串的  首地址//p-字符数组的首地址str  等于str到p之前的元素个数-1,即下标}}size_t string::find(char c, size_t pos ) const{for (int i = pos; i < _size; i++){if (_str[i] == c)return i;}return npos;}//把从pos开始的长度为len的子串  作为一个新的字符串返回string string::substr(size_t pos, size_t len) const{if (len > _size - pos)//如果len大于pos及其之后的所有 字符的长度或者len==npos{string tmp(_str + pos);//直接用默认构造把pos及其之后的字符全部  做新串返回return tmp;}else//否则{//申请长度为len+1的空间,多出的1是给\0的char* p = new char[len + 1];strncpy(p, _str+pos, len);//把从pos开始的长度为len的子串拷贝给pp[len] = '\0';string tmp(p);//用默认构造创建出新字符串return tmp;}}//比较两个字符串的大小//返回值大于0就是  左>右// 返回值等于0就是  左=右// 返回值小于0就是  左<右int string::compare(const string& obj) const{//使用string.h里面的  strcmp即可完成判断return strcmp(_str, obj._str);}string string::operator+(const string& obj)const{string tmp(*this);//拷贝构造出一个临时对象tmp += obj;//让临时对象去+=,就不会改变字符串自己了return tmp;//传值返回}string string::operator+(char c)const{string tmp(*this);//拷贝构造出一个临时对象tmp += c;return tmp;}bool string::operator>(const string& obj)const{if (compare(obj) > 0)//返回值大于0就是  左>右//即调用函数的对象>传入的对象return true;elsereturn false;}bool string::operator<(const string& obj)const{if (!(*this >= obj))// < 就是>=取反{return true;}elsereturn false;}bool string::operator>=(const string& obj)const{//大于等于  是  大于或者等于if (*this > obj || *this == obj)return true;elsereturn false;}bool string::operator<=(const string& obj)const{//小于等于就是  不大于,即大于取反if (!(*this > obj))return true;elsereturn false;}bool string::operator==(const string& obj)const{if (compare(obj) == 0)//返回值大于0就是  左=右//即调用函数的对象=传入的对象return true;elsereturn false;}bool string::operator!=(const string& obj)const{//不等于就是  等于取反if (!(*this==obj))return true;elsereturn false;}//ostream是输出流对象,比如我们常用的cout就是  ostream实例化的对象ostream& operator<< (ostream& os, const string& obj){const char* p = obj.c_str();//获取string对象中的 str成员os << p;//可以用字符指针直接输出  它指向  的字符串return os;//为支持链式编程,返回输出流对象的引用}//istream是输入流对象,比如我们常用的  cin  就是  istream实例化的对象istream& operator>>(istream& is, string& obj){char str[100];//存储  输入的  每一行的字符int sum = 0;//使用sum记录输入的  每一行  的字符个数char c = 0;//\n是换行符(回车),所以  没遇到  \n就  没换行while ((c = is.get()) != '\n')//一次读取一个字符{if (sum == 99)//当sum等于99时说明  str数组存不下了{str[sum] = '\0';//末尾加上了\0才是字符串obj += str;//使用+=把str数组中的字符先加上去sum = 0;//把sum置成0,继续记录}else//否则{//把读取到的字符存进str数组里str[sum++] = c;}}//sum!=0说明最后输入的最后一行字符个数  小于99个,没有+=上if (sum != 0){str[sum] = '\0';//末尾加上了\0才是字符串obj += str;//使用+=把str数组中的字符先加上去}return is;//为支持链式编程,返回  输入流对象  的引用}
}

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

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

相关文章

高中数学:复数-基础概念及运算法则

一、定义 规定 复数集与实数集之间的关系 二、复数的几何意义 第一种几何意义 第二种几何意义 复数向量的模 共轭复数 三、四则运算 加法 复向量加法 减法 两复数的距离 乘法 除法 四、总结 复数的所有运算法则和实数相同。 向量运算和实数向量运算相同。 怎么简便记忆了&a…

Java SE入门及基础(58) 并发 进程与线程概念

目录 并发 进程和线程 1. 进程和线程 2. 进程 3.线程 总结 并发 并发(Concurrency) Computer users take it for granted that their systems can do more than one thing at a time. They assume that they can continue to work in a word processor, while other app…

大模型火了一年半,AI还在「钻木取火」?

伴随着AI大模型的新一轮进化&#xff0c;这个夏天&#xff0c;人工智能正在引领一波新的热潮。 美国当地时间6月18日&#xff0c;AI大模型的主要显卡芯片供应商英伟达收涨3.51%&#xff0c;市值升至3.34万亿美元&#xff0c;一度超越微软和苹果等科技巨头&#xff0c;成为全球…

ElasticSearch中的BM25算法实现原理及应用分析

文章目录 一、引言二、BM25算法实现原理BM25算法的实现原理1. 词频&#xff08;TF&#xff09;&#xff1a;2. 逆文档频率&#xff08;IDF&#xff09;&#xff1a;3. 长度归一化&#xff1a;4. BM25评分公式&#xff1a; BM25算法示例 三、BM25算法在ElasticSearch中的应用分析…

在 Java 中的使用Selenium 测试框架

Selenium 测试框架&#xff1a;在 Java 中的使用 Selenium 测试框架就是这样一个强大的工具&#xff0c;它为 Web 应用的自动化测试提供了全面且高效的解决方案。 一、Selenium 简介 Selenium 是一个开源的自动化测试工具集&#xff0c;专门用于测试 Web 应用程序。它支持多…

数据结构:队列详解 c++信息学奥赛基础知识讲解

目录 一、队列概念 二、队列容器 三、队列操作 四、代码实操 五、队列遍历 六、案例实操 题目描述&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 详细代码&#xff1a; 一、队列概念 队列是一种特殊的线性…

【单片机毕业设计选题24032】-基于STM32的电瓶车电池检测系统

系统功能: 系统上电后显示“欢迎使用电池检测系统请稍后”后两秒后正常显示界面 第一页面第一行显示“系统状态信息” 第二行显示获取到的电压值 第三行显示获取到的电流值 第四行显示获取到的温度和剩余电量值 短按B4按键可切换到第二页面 第二页面第一行显示“温度阈值…

2020年全国大学生数学建模竞赛C题中小微企业信贷决策(含word论文和源代码资源)

文章目录 一、部分题目二、部分论文三、部分源代码&#xff08;一&#xff09;数据处理代码&#xff08;二&#xff09;熵权法与TOPSIS代码&#xff08;三&#xff09;最小二乘法代码&#xff08;四&#xff09;粒子群代码 四、完整word版论文和源代码&#xff08;两种获取方式…

Nest 的 IoC 机制

后端系统中&#xff0c;会有很多对象&#xff1a; Controller 对象&#xff1a;接收 http 请求&#xff0c;调用 Service&#xff0c;返回响应 Service 对象&#xff1a;实现业务逻辑 Repository 对象&#xff1a;实现对数据库的增删改查 此外&#xff0c;还有数据库链接对…

内外网文件流转场景日益复杂,看麒麟信安如何构筑安全防线?

随着信息化快速发展&#xff0c;数据已成为企业核心资产&#xff0c;根据信息安全分级保护和等级保护的相关要求&#xff0c;诸多单位都采取了内外网隔离措施以确保信息安全。但在管理内外部数据流通时&#xff0c;用户单位在集中加密存储、文件流转管理机制、外带文件审批管理…

AI原力觉醒:华硕NUC组团出道,快来Pick属于你的NUC

NUC 家族组团出道&#xff0c;全新的计算体验&#xff0c;重新定义桌面设备。AI加持下&#xff0c;谁最适合你&#xff1f; 颜值担当 NUC 14 Pro 居家必备单品 适用于广大消费者的NUC 14 Pro&#xff0c;不仅颜值在线&#xff0c;更多方位考虑您的日常所需&#xff0c;工作娱…

2024/6/28 英语每日一段

The Supreme Court on Thursday rejected a challenge to an obscure provision of President Donald Trump’s 2017 tax package, ending a lawsuit that many experts feared could destabilize the nation’s tax system. In a divided decision, the court upheld a one-ti…

基于SpringBoot养老院管理系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

Java网络编程(JavaWeb的基础)

Java网络编程&#xff08;JavaWeb的基础&#xff09; 文章目录 Java网络编程&#xff08;JavaWeb的基础&#xff09;前言一、网络编程概述1.1 软件架构&网络基础1.2 网络通信要素:IP/端口/通信协议1.3 传输层协议:tcp/udp 二、网络编程API2.1 InetAddress类2.2 Socket类&am…

控制台扫雷(C语言实现)

目录 博文目的实现思路项目创建文件解释 具体实现判断玩家进行游戏还是退出扫雷棋盘的确定地图初始化埋雷玩家扫雷的实现雷判断函数 源码game.cgame.h扫雷.c 博文目的 相信不少人都学习了c语言的函数&#xff0c;循环&#xff0c;分支那我们就可以写一个控制台的扫雷小游戏来检…

中小企业进行数字化转型会面临哪些挑战?

在当今这个信息化、数字化的时代&#xff0c;中小企业进行数字化转型已不再是选择&#xff0c;而是必然。然而&#xff0c;这条转型之路并非坦途&#xff0c;它充满了未知与挑战。今天&#xff0c;我们就来探讨一下中小企业为社么要进行数字化转型以及在数字化转型过程中可能遇…

1983springboot VUE兼职招聘管理系统开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot VUE兼职招聘管理系统是一套完善的完整信息管理类型系统&#xff0c;结合springboot框架和VUE完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和…

重磅!免费一键批量混剪工具它来了,一天上万短视频不是梦

很多做短视频营销的朋友需要批量生成大量的短视频&#xff0c;但是市面上的工具一是不好用&#xff0c;二是要收费。 今天给大家介绍一款免费的&#xff0c;可以自动化批量生成短视频的工具MoneyPrinterPlus。 同时支持windows和linux平台。 有了它&#xff0c;一天生成上万短…

从零创建深度学习张量库,支持gpu并行与自动微分

多年来&#xff0c;我一直在使用 PyTorch 构建和训练深度学习模型。尽管我已经学会了它的语法和规则&#xff0c;但总有一些东西激起了我的好奇心&#xff1a;这些操作内部发生了什么&#xff1f;这一切是如何运作的&#xff1f; 如果你已经到这里&#xff0c;你可能也有同样的…

Linux 的启动流程

第一步、加载内核 操作系统接管硬件以后&#xff0c;首先读入 /boot 目录下的内核文件。 以我的电脑为例&#xff0c;/boot 目录下面大概是这样一些文件&#xff1a; $ ls /bootconfig-3.2.0-3-amd64config-3.2.0-4-amd64grubinitrd.img-3.2.0-3-amd64initrd.img-3.2.0-4-amd6…