C++string类重要函数模拟实现

为了和C++标准库区分,以下代码除主函数外均在namespace空间

目录

一.成员

二、带参构造函数

三、拷贝构造函数和赋值运算符重载

四、析构函数

 五、重要成员函数实现

1. c_str函数

2. operator[]重载

3. size函数和capacity函数

4.reverse函数

5. push_back和append函数

6. operator+=重载

7. insert函数

8. erase函数 

9. clear函数 

10.resize函数 

11.find函数 

12.substr函数 

六、迭代器

七、全局函数

1.operator判断符重载

2.流输入和流输出运算符重载


一.成员

首先一定要有个字符指针 _str,指向所存放的字符串。

还需要有字符串的大小_size,能够快速的知道这个字符串多大。

再需要有字符串的容量_capacity,知道字符串追加后是否需要扩容。

因此我们定义

class string
{
private:char* _str;size_t _size;size_t _capacity;   
}

二、带参构造函数

构造函数使用的是字符串来构造,并且给缺省值  ""  ,注意里面没有空格,为了能够进行默认构造,并且这是一个字符串,虽然没有给到什么内容,但会存放一个'\0'。

_size和_capacity的大小就是str的长度。

_str需要使用new来开辟空间,开辟空间的大小是_capacity+1,因为之前strlen(str)算出来的长度都没有算'\0',因此你需要多开辟一个空间来存放'\0';

string(const char* str = ""):_size(strlen(str)),_capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}

三、拷贝构造函数和赋值运算符重载

为了实现深拷贝,就需要自己写拷贝构造和赋值运算符重载。

这里的拷贝构造函数和赋值我们选用了很简单的写法,使用带参构造函数构造出一个临时的类对象tmp,这个tmp对象的值正好是当前对象需要的,因此将他们两个交换一下,当前对象就完成了深拷贝了,同时tmp也会出了作用域也会自动调用析构函数进行析构(析构函数马上就写)。

赋值运算符重载也是利用的这种思想,甚至更加简单粗暴,不用为了节约而传 const string& s了,直接传string  tmp,这样会调用一次拷贝构造,我要的就是拷贝出来的临时对象跟我互换,换了之后返回*this 即可

void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}string(const string& s):_str(nullptr),_size(0),_capacity(0)			//保证编译器一定初始化,析构才不会出错
{string tmp(s._str);swap(tmp);
}
string& operator=(string tmp)
{swap(tmp);return *this;
}

四、析构函数

析构函数非常简单,只需要将_str里面的内容delete掉即可,注意是需要delete[] ,顺便将_str置空, _size  和 _capacity赋值给0。

~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}

 五、重要成员函数实现

1. c_str函数

c_str()是将string类对象转化为字符串的格式,方便我们打印操作.

代码只需返回_str即可。

const char* c_str() const
{return this->_str;
}

2. operator[]重载

为了让string类对象像数组一样访问,我们需要重载 [] 

代码也很简单。这里写了两个版本,一个普通版一个const版,这为了让普通对象调用可以修改,const类对象的调用不能修改

char& operator[](size_t pos)
{assert(pos >= 0 && pos <= _size);return _str[pos];
}
const char& operator[](size_t pos) const
{assert(pos >= 0&&pos<=_size);return _str[pos];
}

 3. size函数和capacity函数

让类外访问到private成员_size和_capacity的值。

size_t size() const 
{return _size;
}
size_t capacity() const
{return _capacity;
}

 4.reverse函数

reverse是扩容,如果n比_capacity小就不管,比_capacity打就按照n的大小来扩容。

流程为开辟空间,拷贝数据,释放空间,指向新空间,_capacity变为0

void reverse(size_t n)
{if (n > _capacity){char* tmp = new char[n+1];//要多开一个留给'\0'strcpy(tmp, _str);if(_str!=nullptr)delete[] _str;_str = tmp;_capacity = n;}
}

5. push_back和append函数

push_back是在尾部插入一个字符,插入之前判断空间是否足够,不足就扩容再插入,_size需要++,注意后面要置为'\0'。

append是在尾部插入一个字符串,依然要判断是否扩容,使用了库函数来进行拷贝,strcpy会拷贝'\0',因此无需关心'\0',将_size+=len即可

void push_back(char ch)
{if (_size == _capacity){reverse(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size] = ch;_size++;_str[_size] = '\0';
}
void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reverse(_size + len);}strcpy(_str + _size, str);_size += len;
}

 6. operator+=重载

使用+=比push_back和append爽的多,还不需要关心是字符还是字符串,因为我们运算符重载加函数重载了。

代码部分分别调用即可。

string& operator+=(char ch)
{push_back(ch);return *this;
}
string& operator+=(const char* str)
{append(str);return *this;
}

 7. insert函数

insert函数运用了重载,可以在你想要的位置插入字符和字符串。

他的主要思想是从后往前遍历,并且将数据往后面挪动,直到到了pos位置才会停止,然后将要插入的字符或者字符串放在pos位置即可。

只需要注意字符移动一格,字符串移动len格。

void insert(size_t pos,char ch)
{assert(pos <= _size && pos >= 0);if (_size == _capacity){reverse(_capacity == 0 ? 4 : 2 * _capacity);}for(size_t i = _size + 1; i > pos; i--){_str[i] = _str[i - 1];}_str[pos] = ch;_size++;
}
void insert(size_t pos, const char* str)
{assert(pos <= _size && pos >= 0);size_t len = strlen(str);if (len+_size > _capacity){reverse(len + _size);}for (size_t i = _size + len; i > pos; i--){_str[i] = _str[i  - len];}memcpy(_str + pos, str, len);_size += len;
}

8. erase函数 

erase函数要分两种情况

一种是len的长度要大于等于_size-pos,也就是说要将pos后面的内容全部删除,这种我们处理起来很简单,将pos位置直接置为'\0',同时_size = pos即可。

另一种是后面还有字符需要保留,需要将后面的字符挪动到pos位置这里来,再_size -= len;

这里参数缺省值npos是静态成员函数,类型为size_t,值为-1,代表int的最大值

void erase(size_t pos,size_t len = npos)
{assert(pos < _size && pos >= 0);if (len >= _size - pos){_size = pos;_str[_size] = '\0';}else{for (size_t i = pos + len; i <= _size; i++){_str[i - len] = _str[i];}_size -= len;}
}

 9. clear函数 

clear函数简单,不需要处理_capacity,只需要第一个字符给到'\0',将_size给0。

void clear()
{_str[0] = '\0';_size = 0;
}

10.resize函数 

resize也是两种情况

n小于等于_size时,证明_size要减小,直接在n这个位置给到'\0',_size给0就好

另一种情况就可能需要扩容了,我们直接暴力处理,不管你扩不扩容,先来个reverse(n),要扩容我就扩容,不需要我就返回继续执行后续代码,同时给后面的空间都赋值ch。不要忘记最后_size给0,还有最后给'\0'。

void resize(size_t n,char ch = '\0')
{if (n <= _size){_str[n] = '\0';_size = n;}else{reverse(n);for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}
}

11.find函数 

find函数重载了,可以从某位位置往后找字符,或者字符串。

找字符很简单,一个循环完事。

找字符串用到了strstr()函数,不为空代表找到了,就返回找到的指针- _str指针,就能算出他们两个的差值,就是索引,指针为空就返回 npos。

size_t find(char ch,size_t pos = 0)
{for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;
}	
size_t find(const char* str, size_t pos = 0)
{const char* p = strstr(_str + pos, str);if (p){return p - _str;}return npos;
}

12.substr函数 

substr是从pos位置,截取n个长度的字符串,返回类型为string

如果n == pos||n>=_size-pos即代表pos后面的长度都要截取到,算出pos后面还有多少个字符,给s开辟好空间,遍历直接直接+=即可。

另一种情况,就遍历到 pos + n,再进行+=

string substr(size_t pos, size_t n = npos)
{string s;if (n == npos || n  >= _size - pos){n = _size - pos;s.reverse(n);for (size_t i = pos; i < _size; i++){s += _str[i];}}else{s.reverse(n);for (size_t i = pos; i < pos + n; i++){s += _str[i];}}return s;
}

六、迭代器

迭代器是STL的特性,string类虽然比STL要早一点,还后面还是支持了迭代器,只不过因为该有的功能都有了,迭代器没有那么重要,但是可以为我们后续学其他容器打好基础。

string类的迭代器非常简单,就是一个指针,只需要写好begin() 和 end()就可以进行范围for的遍历了,指针本身就支持++和解引用,因此不需要重载。

我们写了两个迭代器,普通迭代器可以修改,const 迭代器不能修改

typedef char* iterator;
typedef const char* const_iterator;
iterator begin() 
{return _str;
}
iterator end()
{return _str + _size;
}
const_iterator begin() const
{return _str;
}
const_iterator end() const 
{return _str + _size;
}

七、全局函数

1.operator判断符重载

调用strcmp,再加复用

下面6种判断符重载,可以写在类里面,但string类为了支持宽字符等其他字符,写出了全局函数,这里只支持char类型,但还是写出了全局函数

bool operator<(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) < 0;
}
bool operator>(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) > 0;
}
bool operator==(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) == 0;
}
bool operator<=(const string& s1, const string& s2)
{return !(s1>s2);
}
bool operator>=(const string& s1, const string& s2)
{return !(s1<s2);
}
bool operator!=(const string& s1, const string& s2)
{return !(s1 == s2);
}

 2.流输入和流输出运算符重载

流输出非常简单,直接遍历即可。

这里使用get()函数才可以获取到' '(空格)和'\n'(换行) 

我们使用了临时数组来存储,目的是为了减少扩容,因为扩容的消耗很大。一个一个放到数组里面,等获取到' '(空格)和'\n'(换行) ,或者数据满了之后,再从数组里面将数据提取出来。

ostream& operator<<(ostream& out, const string& s)
{//out << s.c_str() << endl;for (auto e : s){out << e;}return out;
}
istream& operator>>(istream& in,string& s)
{s.clear();char tmp[129];size_t i = 0;char ch;ch = in.get();while (ch != ' ' && ch != '\n'){tmp[i++] = ch;if (i == 128){tmp[i] = '\0';s += tmp;i = 0;}ch = in.get();}if (i != 0){tmp[i] = '\0';s += tmp;}return in;
}

到此,我们程序终于完成了,最后放上总代码 

string.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <cassert>
#include <iostream>
using namespace std;
namespace kky
{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin() {return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const {return _str + _size;}string(const char* str = ""):_size(strlen(str)),_capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}//传统//string(const string& s)//	:_size(s._size)//	,_capacity(s._capacity)//{//	_str = new char[_capacity + 1];//	strcpy(_str, s._str);//}//string& operator=(const string& s)//{//	if (this != &s)//	{//		char* tmp = new char[s._capacity + 1];//		strcpy(tmp, s._str);//		delete[] _str;//		_str = tmp;//		_size = s._size;//		_capacity = s._capacity;//	}//	return *this;//}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}string(const string& s):_str(nullptr),_size(0),_capacity(0)			//保证编译器一定初始化,析构才不会出错{string tmp(s._str);swap(tmp);}string& operator=(string tmp){swap(tmp);return *this;}const char* c_str() const{return this->_str;}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}size_t size() const {return _size;}size_t capacity() const{return _capacity;}char& operator[](size_t pos){assert(pos >= 0 && pos <= _size);return _str[pos];}const char& operator[](size_t pos) const{assert(pos >= 0&&pos<=_size);return _str[pos];}size_t find(char ch,size_t pos = 0){for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}	size_t find(const char* str, size_t pos = 0){const char* p = strstr(_str + pos, str);if (p){return p - _str;}return npos;}string substr(size_t pos, size_t n = npos){string s;if (n == npos || n  >= _size - pos){n = _size - pos;s.reverse(n);for (size_t i = pos; i < _size; i++){s += _str[i];}}else{s.reverse(n);for (size_t i = pos; i < pos + n; i++){s += _str[i];}}return s;}void reverse(size_t n){if (n > _capacity){char* tmp = new char[n + 1];//要多开一个留给'\0'strcpy(tmp, _str);if (_str != nullptr)delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size == _capacity){reverse(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reverse(_size + len);}strcpy(_str + _size, str);_size += len;}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}void insert(size_t pos,char ch){assert(pos <= _size && pos >= 0);if (_size == _capacity){reverse(_capacity == 0 ? 4 : 2 * _capacity);}for(size_t i = _size + 1; i > pos; i--){_str[i] = _str[i - 1];}_str[pos] = ch;_size++;}void insert(size_t pos, const char* str){assert(pos <= _size && pos >= 0);size_t len = strlen(str);if (len+_size > _capacity){reverse(len + _size);}for (size_t i = _size + len; i > pos; i--){_str[i] = _str[i  - len];}memcpy(_str + pos, str, len);_size += len;}void erase(size_t pos,size_t len = npos){assert(pos < _size && pos >= 0);if (len >= _size - pos){_size = pos;_str[_size] = '\0';}else{for (size_t i = pos + len; i <= _size; i++){_str[i - len] = _str[i];}_size -= len;}}void clear(){_str[0] = '\0';_size = 0;}void resize(size_t n,char ch = '\0'){if (n <= _size){_str[n] = '\0';_size = n;}else{reverse(n);for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}}private:char* _str;size_t _size;size_t _capacity;//const static size_t npos = -1; //特例const static size_t npos;};const size_t string::npos = -1;ostream& operator<<(ostream& out, const string& s){//out << s.c_str() << endl;for (auto e : s){out << e;}return out;}istream& operator>>(istream& in,string& s){s.clear();char tmp[129];size_t i = 0;char ch;ch = in.get();while (ch != ' ' && ch != '\n'){tmp[i++] = ch;if (i == 128){tmp[i] = '\0';s += tmp;i = 0;}ch = in.get();}if (i != 0){tmp[i] = '\0';s += tmp;}return in;}bool operator<(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}bool operator>(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) > 0;}bool operator==(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) == 0;}bool operator<=(const string& s1, const string& s2){return !(s1>s2);}bool operator>=(const string& s1, const string& s2){return !(s1<s2);}bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}void test01(){string s("123456");cout << s.c_str() << endl;s[0] = 'c';cout << s.c_str() << endl;/*for (int i = 0; i < s.size(); i++){cout << s[i] << " ";}cout << endl;string::iterator it = s.begin();while (it != s.end()){cout << *it << " ";it++;}cout << endl;for (auto e : s){cout << e << " ";}*/}void test02(){string s1("hello world");cout << s1.c_str() << endl;s1.push_back('s');s1.append("tring");cout << s1.c_str() << endl;s1 += 'w';s1 += "odema";cout << s1.c_str() << endl;}void test03(){string s1("hello world");cout << s1.c_str() << endl;s1.insert(5, 'c');cout << s1.c_str() << endl;s1.insert(s1.size(), 'c');cout << s1.c_str() << endl;s1.insert(0, 'c');cout << s1.c_str() << endl;s1.insert(5, "hhh");cout << s1.c_str() << endl;s1.erase(0, 3);cout << s1.c_str() << endl;s1.erase(s1.size()-1, 3);cout << s1.c_str() << endl;}void test04(){string s1("hello world");cout << s1.c_str() << endl;string s2("hello worldl");cout << (s1 != s2) << endl;;cout << s2 << endl;cin >> s1;cout << s1.c_str() << endl;}void test05(){string s1("hello world");cout << s1.c_str() << endl;string s2("hello worldl");cout << s2 << endl;s1.resize(5);cout << s1 << endl;s1.resize(15,'c');cout << s1 << endl;}void test06(){string s1("hello world");string s2(s1);cout << s1 << endl;cout << s2 << endl;string s3 = "wasg1";s3 = s1;cout << s3 << endl;}
}

test.cpp  (调用测试接口即可) 

#include"string.h"
int main()
{kky::test06();}

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

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

相关文章

【解决】运行vue项目,启动报错 in ./node_modules/@intlify/core-base/dist/core-base.cjs

我的处理方式: 一开始查了好多方法&#xff0c;删除node_modules&#xff0c;重新安装&#xff0c;切换node版本等&#xff0c;但是发现并没有用 之后来发现是安装依赖包的时候有些包安装失败导致的&#xff0c;只要有针对性的重新安装依赖就可以了 例如&#xff1a; in ./n…

Splashtop 与 Canopy 携手共同增强对物联网设备的远程管理

2023年10月17日 加利福尼亚州库比蒂诺 Splashtop 在安全远程访问解决方案领域处于领先地位&#xff0c;Canopy 则是用于复杂硬件部署的领先 RMM 平台&#xff0c;双方今天宣布达成战略合作伙伴关系&#xff0c;以进一步增强和简化对物联网设备的远程管理。通过此次合作&#x…

JSX的本质

一、本质 React.createElement即h函数&#xff0c;返回vnode第一个参数&#xff0c;可能是组件&#xff0c;也可能是html tag组件名&#xff0c;首字母必须大写&#xff08;React规定&#xff09; 二、babel试一试 &#xff08;babel集成了jsx的编译环境&#xff09; // JSX…

Linux生产者消费者模型

生产者消费者模型 生产者消费者模型生产者消费者模型的概念生产者消费者模型的特点生产者消费者模型优点 基于BlockingQueue的生产者消费者模型基于阻塞队列的生产者消费者模型模拟实现基于阻塞队列的生产消费模型 生产者消费者模型 生产者消费者模型的概念 生产者消费者模式就…

Spring framework Day 23:容器事件

前言 容器事件是 Spring Framework 中的一个重要概念&#xff0c;它提供了一种机制&#xff0c;使我们能够更好地了解和响应 Spring 容器中发生的各种事件。通过容器事件&#xff0c;我们可以在特定的时间点监听和处理容器中的各种状态变化、操作和事件触发&#xff0c;以实现…

Go 存储系列:LSM存储引擎 LevelDB

概念介绍 LSM-Tree 被是一种面向写多读少应用场景的数据结构 &#xff0c;被 Hbase、RocksDB 等强力 NoSQL 数据库采用作为底层文件组织方式。 简单的LSM-Tree 包含 2 层树状数据结构&#xff1a; Memtable 并完全驻留在内存中&#xff08;假设 T0&#xff09; SStables 存储…

Unity SRP 管线【第二讲:Draw Call】

参考&#xff1a; https://edu.uwa4d.com/lesson-detail/282/1309/0?isPreview0 文章目录 参考&#xff1a;一、Shader1.HLSL引入2.获取Unity提供的标准输入3.Unity提供的运算库SpaceTransform库的宏对应补充&#xff1a; 4.标准库Common.hlsl5.SpaceTransforms库引入Commo…

【笔记】Endnote20插入文献

方法一 1.首先选中要参考的文章 2.在word里选好格式 3.在word里点击插入已选文献 前提&#xff1a;已经将光标放在要插入的位置了 4.插入文献即可&#xff0c;效果如下 方法二&#xff08;方便些&#xff0c;但是word容易闪退&#xff09; 1.点击要插入的文献&#xff0c;…

Apache Log4j Server (CVE-2017-5645) 反序列化命令执行漏洞

文章目录 Apache Log4j Server 反序列化命令执行漏洞&#xff08;CVE-2017-5645&#xff09;1.1 漏洞描述1.2 漏洞复现1.2.1 环境启动1.2.2 漏洞验证1.2.3 漏洞利用 1.3 加固建议 Apache Log4j Server 反序列化命令执行漏洞&#xff08;CVE-2017-5645&#xff09; 1.1 漏洞描述…

蓝绿发布,灰度发布,滚动发布

写在前面 本文看下生产环境中有哪些常用的发布策略。 1:蓝绿发布 蓝绿发布要求将线上机器分成逻辑上的AB两&#xff08;蓝绿就是两种颜色&#xff09;组&#xff0c;升级时先将A组从负载均衡中摘除&#xff0c;由B组对外提供服务&#xff0c;如下图&#xff1a; 当A组升级…

嵌入式学习笔记(59)内存管理之结构体

数据结构&#xff1a;是一门研究数据在内存中如何分布的学问。 1.5.1.最简单的数据结构&#xff1a;数组 数组的特点&#xff1a;类型相同、意义相关 数组的优势&#xff1a;数组比较简单&#xff0c;访问使用下标&#xff0c;可以随机访问&#xff08;就是可以通过下标随机…

【迎战2023双十一】小白也能玩转!手把手教你实时获取多平台店铺数据,轻松实现数据大屏展示

要实时获取多平台店铺数据进行数据大屏展示&#xff0c;需要进行以下步骤&#xff1a; 确定数据采集方式&#xff1a;通过爬虫程序&#xff08;如Python的BeautifulSoup、Scrapy等爬虫框架&#xff09;或API接口来实现数据的获取&#xff0c;确定该方法所需的数据格式和调用方…

字节码增强技术-ASM

概述 在Java中一般是用javac命令编译源代码为字节码文件&#xff0c;一个.java文件从编译到运行的示例如图所示: 使用字节码的好处&#xff1a;一处编译&#xff0c;到处运行。java 就是典型的使用字节码作为中间语言&#xff0c;在一个地方编译了源码&#xff0c;拿着.class …

Error: GlobalConfigUtils setMetaData Fail Cause:java.lang.NullPointerException

文章目录 1、在开发中会出现这样的错误。2、其次&#xff0c;再看其他错误&#xff1a; 1、在开发中会出现这样的错误。 完整错误&#xff1a;Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Error: GlobalConfigUtils setMetaData Fail ! Cause…

pyflink 环境测试以及测试案例

1. py 的 环境以来采用Anaconda环境包 安装版本&#xff1a;https://www.anaconda.com/distribution/#download-section Python3.8.8版本&#xff1a;Anaconda3-2021.05-Linux-x86_64.sh 下载地址 https://repo.anaconda.com/archive/ 2. 安装 bash Anaconda3-2021.05-Linux-x…

送水订水商城小程序的作用是什么

无论瓶装水还是桶装水在市场中的需求度总是很高&#xff0c;相关送水公司或零售水企业也不少&#xff0c;消费者的购物方式一般是品牌直售或通过经销商&#xff0c;零售水则是超市/商场等场景。随着人们健康品质生活提升&#xff0c;家庭或办公等场所对桶装水或瓶装水的需求日益…

群硕与Microsoft Dynamics全球团队密切协作,加速ERP产品迭代

群硕具备强大的软件研发能力&#xff0c;搭建自动化测试平台&#xff0c;保证高质量交付。 ERP系统的引入被视为企业走向数字化转型的关键一步。 此系统有助于实现企业内部资源与外部资源的整合&#xff0c;通过软件把人、财、物、产、供、销及相应的物流、信息流、资金流、管…

找不到msvcr120.dll怎么办?msvcr120.dll丢失如何修复?

MSVCR120.dll是一个动态链接库文件&#xff0c;它是Microsoft Visual C 2012 Redistributable Package的一部分。这个文件包含了许多用于运行C应用程序的函数和类。当我们的计算机上缺少这个文件时&#xff0c;就会导致一些程序无法正常运行&#xff0c;甚至会出现系统崩溃的情…

基于当量因子法、InVEST、SolVES模型等多技术融合在生态系统服务功能社会价值评估中的应用及论文写作、拓展分析

查看原文>>>基于当量因子法、InVEST、SolVES模型等多技术融合在生态系统服务功能社会价值评估中的应用及论文写作、拓展分析 生态系统服务是人类从自然界中获得的直接或间接惠益&#xff0c;可分为供给服务、文化服务、调节服务和支持服务4类&#xff0c;对提升人类福…

黑豹程序员-架构师学习路线图-百科:Maven

文章目录 1、什么是maven官网下载地址 2、发展历史3、Maven的伟大发明 1、什么是maven Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project’s build, reporting and…