C++ --- string类的简单实现

article/2025/7/5 4:01:48

string类的简单实现

  • 前言
  • 1、基本成员
  • 2、构造方法和析构方法
    • 2.1无参构造
    • 2.2有参构造
    • 2.3析构函数
    • 2.4拷贝构造函数
  • 3、遍历方式
    • 3.1operator [ ]
    • 3.2iterator
      • 3.2.1正向迭代器
      • 3.2.2const正向迭代器
    • 3.3范围for
  • 4、常用方法,运算符重载
    • c_str()
    • size()
    • reverse()
    • push_back()
    • pop_back()
    • append()
    • operator+=
    • operator<<
    • insert()
    • eryes()
    • find()
    • substr()
    • 关系运算符重载
    • operator=
    • string::swap()
    • swap()

前言

本文结合C语言的函数,对其C++的STL中string类的常用接口进行简单实现,以更好的通过底层来理解string类。
本文实现的方法都是进行了声明定义分离。

1、基本成员

通过对于string类的学习,我们知道string类的基本成员变量是包含一个指向数组的指针变量,一个记录字符串大小的变量。一个记录字符串容量大小的变量。

代码如下:

namespace dyj   
{class string{// 成员变量private:char* _str;         // 指向数组的指针int _size;          // 记录字符串大小int _capacity;      // 记录字符串容量// 成员函数public:// ......};
}

上述代码片段使用了命名空间,这里使用命名空间怕和库中的string冲突,或者和外部其他代码命名冲突,所以使用命名空间将我自己的string圈起来,这样就不会产生命名冲突了。

2、构造方法和析构方法

2.1无参构造

在使用无参构造的时候,size与capacity都是0,并且大小都是不包含\0字符的,所以初始化成0;但是str有一点不一样,str是指向数组的指针,这个字符数组和C语言的字符数组一样,末尾是存在一个\0的,用来标识字符串的结束,故而不能来用nullptr来初始化。

代码如下:

	// 无参构造string::string():_str(new char[1]{'\0'}),_size(0),_capacity(0){}

2.2有参构造

这里有参构造,_str(char*)若直接使用传递过来的str(const char*)进行初始化,则是一个权限的放大,所以不能直接进行初始化,所以思路是先动态申请和str相同大小的空间,申请空间后使用strcpy函数进行拷贝,从而完成初始化工作;_size和_capacity使用的是传递过来的str的大小来初始化。

代码如下:

	// 有参构造string::string(const char* str):_size(strlen(str)){_str = new char[_size + 1];_capacity = _size;// strcpy函数是会靠拷贝 \0 的strcpy(_str, str);}

上述代码使用了初始化列表和函数体内部初始化混合使用,并且_str和_capacity的大小由_size替换,这样可以只使用一次strlen函数,从而提高效率。

创建一个由有参构造生成的对象:
在这里插入图片描述
也验证了string类的对象串的末尾也和C语言一样存在一个\0字符。

其实有参和无参的构造可以合并成一个,使用缺省值即可。
合并代码:

	// 无参有参相结合 --- 缺省参数string(const char* str = "");     // 在这里加一个空字符即可

这里的缺省值不能是\0字符,\0字符是一个char类型的,不能转换成const char*类型。

2.3析构函数

代码如下:

	// 析构函数string::~string(){delete[] _str;       // 配对使用_str = nullptr;_size = 0;_capacity = 0;}

这里_str使用的是动态开辟数组的形式进行初始化,所以这里析构的时候要配对使用。

2.4拷贝构造函数

代码如下:

	// 拷贝构造string::string(const string& str){// 传统写法:_str = new char[str._capacity + 1];memcpy(_str, str._str, str._size + 1);_size = str._size;_capacity = str._capacity;// 现代写法:string temp(str._str);    swap(temp);	}

使用构造函数创建中间temp对象,再进行交换,只是表达更加简洁,没有进行效率的提升。
而且这里的swap函数并不是库里重载的全局函数,而是自己写的swap函数。

3、遍历方式

3.1operator [ ]

功能:此重载(有两个版本)是返回底层数组第i个位置所在的字符。

代码如下:

	// operator[] --- 两种版本// 此版本可读可写,服务于普通对象char& string::operator[](size_t i)         {assert(i < _size);            // 可以检查越界操作return _str[i];}// 此版本只可读不可写,服务于const对象const char& string::operator[](size_t i) const   {assert(i < _size);return _str[i];}

测试:
在这里插入图片描述

3.2iterator

功能:此为迭代器,STL中主流的遍历方式。
别看名字高大上,其实底层就是像指针(形态之一)一样的东西。

3.2.1正向迭代器

按照像指针的形式,所以前面有一个重命名:typedef char* iterator;,这个是放在自己实现的string类里面重命名。

代码如下:

	// iteratorstring::iterator string::begin(){// 即返回初始位置的迭代器 --- 下标为0的元素的位置return _str;}string::iterator string::end(){// 即返回末尾位置的迭代器 --- 最后的\0的位置return _str + _size;}

测试:
在这里插入图片描述

3.2.2const正向迭代器

const正向迭代器套路和上述差不多,需要重命名一个:typedef char* const_iterator;,同样定义在自己实现的string类里面。

代码如下:

	// const_iteratorstring::const_iterator string::begin() const{return _str;}string::const_iterator string::end() const{return _str + _size;}

测试:
在这里插入图片描述

3.3范围for

功能:也是一种遍历方式,C++11引入。
其底层是由迭代器实现的。

代码如下:

	// 范围forfor (auto ch : s2)	{std::cout << ch;}std::cout << std::endl;

测试:
在这里插入图片描述

4、常用方法,运算符重载

c_str()

功能:返回其底层指向数组的指针。

代码如下:

	// c_str() --- 返回底层指向数组的指针const char* string::c_str() const{return _str;}

测试:
在这里插入图片描述

size()

功能:返回字符串的大小(不包含\0)。

代码如下:

	// size() --- 返回字符串的大小size_t string::size() const{return _size;}

测试:
在这里插入图片描述

reverse()

功能:进行扩容或者缩容操作,但是这里只实现其扩容功能,缩容的不实现。

代码如下:

	void string::reverse(size_t n){if (n > _capacity){// 手动扩容 --- 手动申请一块新空间char* str = new char[n + 1];      // 加一为了预留\0的空间memcpy(str, _str, n + 1);// 释放旧空间delete[] _str;// 指向新空间_str = str;_capacity = n;}}

第一步的n > _capacity为了进行判断,如果需要的空间大于_capacity,则进行扩容操作,不过C++没有像C语言配套的扩容(realloc)接口,所以就只能自己来手动扩容。

三步骤:
(1)申请一块扩容后的新空间,注意预留 \0 的空间。
(2)将旧空间释放,注意配套使用delete[ ]。
(3)再将指针指向新空间,容量改为新容量。

注意点:拷贝旧字符串到新字符串不要直接使用strcpy函数,因为strcpy函数进行拷贝时检测到 \0 字符后就停止拷贝,我们都知道插入的字符可以是 \0 ,即字符串中间也可能出现 \0,这样一来,若后续还需插入新的字符串或者字符,那后续的内容就拷贝不进去了,所以说这里需要进行一个字符一个字符的拷贝,以免出现上述情况;同样也别忘记末尾的 \0 字符。

push_back()

功能:在字符串末尾插入一个字符。

代码如下:

	// push_back()void string::push_back(char ch){// 判断是否需要扩容 --- size与capacity进行比较if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reverse(newcapacity);}_str[_size++] = ch;_str[_size] = '\0';        // 别忘记\0}

上述代码和我们之前的使用C语言实现基本的数据结构的思路是一样的,先判断是否需要扩容,再将数据插入进串尾,不过需要注意的是串本身带有一个\0,别忘记将其添加在末尾。

pop_back()

功能:尾删一个字符。

代码如下:

	// pop_back()void string::pop_back(){// 不能对空串进行尾删操作assert(_size > 0);--_size;_str[_size] = '\0';}

append()

功能:在字符串末尾插入一字符串,这里只实现插入常量字符串的重载。

代码如下:

	// append()void string::append(const char* str){// 首先记录插入的字符串的长度size_t len = strlen(str);// 再判断是否需要扩容 --- 有插入短串,长串的区别if (_size + len > _capacity){size_t newcapacity = 2 * _capacity > _size + len  ? 2 * _capacity : _size + len;reverse(newcapacity);}// (1)// 将插入的串一个字符一个字符的插入for (size_t i = 0; i < len; i++){_str[_size++] = str[i];}_str[_size] = '\0';       // 最后别忘记 \0// (2)// 或者使用memcpy函数进行拷贝memcpy(_str + _size, str, len + 1);_size += len;             // 添加完字符串后_size会改变大小}

如果插入的是一个短串,push_back()里面的2倍扩容机制是适用的,但是插入长串旧不一样了,若长串特别特别大,那就需要N多次的2倍扩容,效率不高。

所以这里重新设计一个扩容机制:
对插入串之后的大小跟容量进行比较,大于则需要进行扩容,若2倍扩容后大于了插入串之后的大小,则表明插入的是一个较短的串,进行2倍扩容即可;若2倍扩容后小于了插入串之后的大小,则表明插入的是一个较长的串,就扩容插入串之后的大小。

拷贝方法代码中任选其一。

operator+=

功能:字符串末尾插入一个字符或字符串。

代码如下:

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

实质上是对于append和push_back的复用。

operator<<

功能:流插入,用于输出不同类型的对象,其只能重载成为一个全局函数。

代码如下:

	// operator<<ostream& operator<<(ostream& out, const string& str){//out << str.c_str();   // 错误的输出,因为c_str是返回指向的数组,数组一遇到\0就会停止,所以中间若添加// \0字符,则会输出不完全。// 正确的是一个一个输出:for (size_t i = 0; i < str.size(); i++){out << str[i];}return out;}

因为<<运算符的操作数为两个,若重载成成员函数,则this指针会占用左操作数的位置,而左操作数只能是ostream类的对象,不能是string类的对象,第二个参数才是string类的对像,所以这里的<<重载只能是全局函数。

insert()

功能:在pos位置插入字符或者字符串,这里只实现插入单个字符的字符串的重载。

(1)插入单个字符
代码如下:

	// insertvoid string::insert(size_t pos, char ch){assert(pos <= _size);    // 判断pos位置是否合法// 先判断扩容 --- 单个字符两倍扩容足够if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reverse(newcapacity);}// 挪动数据 --- 腾出pos位置size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}// 找到pos位置 --- 插入ch_str[pos] = ch;++_size;}

插入单个字符的图解:
在这里插入图片描述
(2)插入字符串
代码如下:

	void string::insert(size_t pos, const char* str){// p判断pos位置是否合法assert(pos <= _size);size_t len = strlen(str);// 再判断是否需要扩容 --- 插入短串(两倍扩容),长串(需要多少扩多少)if (_size + len > _capacity){size_t newcapacity = 2 * _capacity > _size + len ? 2 * _capacity : _size + len;reverse(newcapacity);}// 挪动数据 --- 找到pos位置size_t end = _size + len;while (end > pos + len -1){_str[end] = _str[end - len];--end;}// 找到了pos位置 --- 插入strfor (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;   // 一样的别忘记了更新大小}

插入字符串的图解:
在这里插入图片描述

eryes()

功能:删除指定位置开始,len长度的字符或者字符串。

代码如下:

	// erase()void string::erase(size_t pos, size_t len){// pos要合法 --- 删除时_size没意义assert(pos < _size);// 当len取缺省值npos,或者len大于pos位置开始后续的串的长度(左闭右开区间,// 直接右减左就是区间长度),此时直接删完。if (len == npos || len >= (_size - pos)){_size = pos;_str[pos] = '\0';}else{size_t i = pos + len;memmove(_str + pos, _str + i, _size + 1 - i);_size -= len;}}

memmove函数图解:
在这里插入图片描述
这里使用memmove函数也可以防止内存重叠。

find()

功能:从字符串中查找满足给定字符或者字符串的起始下标

代码如下:

	// find()// 查找字符size_t string::find(char ch, size_t pos) const{// pos位置要合法assert(pos < _size);for (size_t i = pos; i < _size; i++){// 找到返回下标if (_str[i] == ch){return i;}}// 没找到返回nposreturn npos;}// 查找子串size_t string::find(const char* str, size_t pos) const{// pos位置要合法assert(pos < _size);// 暴力匹配const char* ptr = strstr(_str + pos,str);if (ptr == nullptr){return npos;}else// 指针减指针返回其之间的元素个数,也就是find到的子串的起始下标return ptr - _str;  }

测试:
在这里插入图片描述

substr()

功能:从pos位置开始返回len个字符。

代码如下:

	// substrstring string::substr(size_t pos, size_t len) const{if (len == npos || len >= (_size - pos)){// 取到尾len = _size - pos;}string ret;ret.reverse(len);for (size_t i = 0; i < len; i++){ret += _str[pos + i];}return ret;}

此函数是一个传值返回,所以返回的是ret的拷贝(临时对象),而此string类中还未实现拷贝构造,所以这里使用的是编译器默认生成的拷贝构造,但是,满足不了需求,因为是浅拷贝,会导致ret的拷贝(临时对象)也指向了ret指向的数组,出函数,ret销毁,ret的拷贝指向的数组也销毁了,所以这里成为了野指针的情形,故我们要重新自己写一个深拷贝的构造函数(此函数在上文2.4),来实现需求。

测试:
在这里插入图片描述

关系运算符重载

功能:对string类型实例化的对象进行比较大小,只需要实现(1)和(5)即可,剩下的直接使用这两个来复用。

(1)operator <
代码如下:

	// s1 < s2 --- 比较同一位字符的ASCII值大小bool string::operator<(const string& str) const{size_t i1 = 0, i2 = 0;while (i1 < _size && i2 < str._size){// 一般情况:if (_str[i1] < str[i2]){return true;}else if (_str[i1] > str[i2]){return false;}else{++i1, ++i2;}}// 特殊情况://(1) "hello"   "hello"   --->false//(2)"helloD"  "hello"   --->false//(3) "hello"   "helloD"  --->truereturn i2 < str._size;}

特殊情况下,i2小于自身字符串的长度时,即代表真,也就是_str < str,因为当为特殊情况1时两串前面字符都相同,i1,i2同时结束记录,此时i2等于自身字符串的长度,为假,返回false;当为特殊情况2时,i2先结束记录,此时i2还是等于自身字符串的长度,为假,返回false;当为特殊情况3时,i1先结束记录,此时i2还要字符没有记录,所以i2小于自身字符串的长度,为真,返回true。

(2)operator <=
代码如下:

	bool string::operator<=(const string& str) const{return *this < str || *this == str;}

(3)operator >
代码如下:

	bool string::operator>(const string& str) const{return !(*this <= str);}

(4)operator >=
代码如下:

	bool string::operator>=(const string& str) const{return !(*this < str);}

(5)operator ==
代码如下:

	bool string::operator==(const string& str) const{size_t i1 = 0, i2 = 0;while (i1 < _size && i2 < str._size){if (_str[i1] != str[i2]){return false;}else{++i1, ++i2;}}// i1,i2都走到尾了才为真return i1 == _size && i2 == str._size;}

只有当i1,i2同时记录结束,也就是都等于自身字符串长度时才为真,其余则为假。

(6)operator !=
代码如下:

	bool string::operator!=(const string& str) const{return !(*this == str);}

operator=

功能:对string类型实例化的对象进行赋值操作。

代码如下:

	// operator=string& string::operator=(const string& str){// 不能自己给自己赋值if (this != &str){// 传统写法:char* temp = new char[str._capacity + 1];memcpy(temp, str._str, str._size + 1);delete[] _str;_size = str._size;_capacity = str._capacity;// 现代写法:string temp(str._str);swap(temp);}return *this;}

这里现代写法道理同拷贝构造的现代写法。

string::swap()

功能:交换两个串,这是string类成员函数swap()。

代码如下:

	// swapvoid string::swap(string& str){// 这里的内部swap是库里的全局函数std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}

swap()

功能:交换两个串,这是全局函数swap()。

代码如下:

	// swap()void swap(string& x,string& y){x.swap(y);}

上述两种swap功能上一样,不过定义出来都有意义的,首先算法库里面也是有swap函数的,不过其实现使用了很多的深拷贝以及模板,同常情况下不会使用算法库里的swap,所以就有了成员函数以及全局函数的swap,而且这两个的定义也是为了使用者在不了解有成员函数的情况下写成了全局函数,前面不是说算法库里的swap是模板吗,那么全局函数的swa就是指定了参数,更适合调用,因为有模板和指定参数的情况下,肯定会带调用那个更具体的函数。所以这里重载两种swap都是为了不去调用算法库里面的swap函数。


http://www.hkcw.cn/article/tJMWbOerFE.shtml

相关文章

ESP32之Linux编译环境搭建流程

背景&#xff1a;为了解决 “windows环境中编译ESP32代码速度慢” 的问题&#xff0c;现搭建一个Linux环境&#xff0c;让windows下的VScode连接到Linux环境&#xff0c;VSCode负责编辑代码&#xff0c;虚拟机用于编译代码。 目录 一、安装VMware 1.1 获取VMware安装包 1.2…

Python-matplotlib中的Pyplot API和面向对象 API

matplotlib中的Pyplot API和面向对象 API Pyplot API&#xff08;状态机模式&#xff09;面向对象 API 详解二者差别核心区别方法命名差异注意事项差别举例 &#x1f345; Pyplot API&#xff08;状态机模式&#xff09;和面向对象 API 是两种不同的编程接口.&#x1f345; 它们…

BUUCTF之[ACTF2020 新生赛]BackupFile

打开环境就一句话 找出源文件! 结合题目名字&#xff1a;BackupFile 先用dirsearct扫描网站文件 发现一个index.php.bak ,拼接url下载 打开发现php代码 <?php include_once "flag.php";if(isset($_GET[key])) {$key $_GET[key];if(!is_numeric($key)) {exit…

Spring Boot 3.X 下Redis缓存的尝试(一):初步尝试

背景 想像一下有这么一个场景&#xff0c;一个系统有超多角色、角色下有多个菜单、菜单下有多个按钮权限&#xff0c;这种子父级关系查询每次向数据库查询相当耗时&#xff0c;那么我们是否可以将这种更新频次不高&#xff0c;而查询耗时的数据且不直接影响业务的数据放进缓存中…

基于springboot的民间文化艺术品销售系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业多年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

9 动态规划

9.3 爬楼梯 从1开始举例子发现规律 dp[i]dp[i-1]dp[i-2]; class Solution { public:int climbStairs(int n) {if(n<1){return 1;}vector<int>dp(n1);dp[2]2;dp[1]1;for(int i3;i<n;i){dp[i]dp[i-1]dp[i-2];}return dp[n];} }; 9.29 打家劫舍 1 确定dp数组下标与…

Playwright 测试框架 - Node.js

🚀超全实战:基于 Playwright + Node.js 的自动化测试项目教程【附源码】 📌 本文适合自动化测试入门者 & 前端测试实战者。从零开始手把手教你搭建一个 Playwright + Node.js 项目,涵盖配置、测试用例编写、运行与调试、报告生成以及实用进阶技巧。建议收藏!👍 �…

4.RV1126-OPENCV 图像轮廓识别

一.图像识别API 1.图像识别作用 它常用于视觉任务、目标检测、图像分割等等。在 OPENCV 中通常使用 Canny 函数、findContours 函数、drawContours 函数结合在一起去做轮廓的形检测。 2.常用的API findContours 函数&#xff1a;用于寻找图片的轮廓&#xff0c;并把所有的数…

Cursor从入门到精通实战指南(五):一键生成流程图/架构图,开发者必备收藏!

解锁Cursor&#xff1a;开启高效开发新境界 结合了GPT-4、Claude 3.5等强大的大语言模型&#xff0c;能够通过自然语言交互实现代码生成、原型设计、流程优化等功能。无论是编程新手还是经验丰富的开发者&#xff0c;都能借助Cursor的智能特性&#xff0c;快速完成复杂的编码任…

postman工具使用

基本功能操作 常用断言 定义&#xff1a;postman 断言借助 JavaScript - js 语言编写代码&#xff0c;自动判断预期结果与实际结果是否一致。&#xff08; 注意断言 代码写在 Tests 的标签中&#xff09; 断言响应状态码 断言响应体是否包含某个字符串&#xff08;Response bo…

【Elasticsearch】Elasticsearch 核心技术(一):索引

Elasticsearch 核心技术&#xff08;一&#xff09;&#xff1a;索引 1.索引的定义2.索引的命名规范3.索引的增、删、改、查3.1 创建索引3.1.1 创建空索引 3.2 删除索引3.3 文档操作3.3.1 添加/更新文档&#xff08;指定ID&#xff09;3.3.2 添加文档&#xff08;自动生成ID&am…

玩客云 OEC/OECT 笔记(2) 运行RKNN程序

目录 玩客云 OEC/OECT 笔记(1) 拆机刷入Armbian固件玩客云 OEC/OECT 笔记(2) 运行RKNN程序 RKNN OEC/OEC-Turbo 使用的芯片是 RK3566/RK3568, 这个系列是内建神经网络处理器 NPU 的, 利用 RKNN 可以部署运行 AI 模型利用 NPU 硬件加速模型推理. 要使用 NPU, 首先需要在电脑使…

【音视频】FFmpeg 硬件(NVDIA)编码H264

FFmpeg 与x264的关系 ffmpeg软编码是使⽤x264开源项⽬&#xff0c;也就是说ffmpeg软编码H264最终是调⽤了x264开源项⽬&#xff0c;所以我们要先理解ffmpeg和x264的调⽤关系&#xff0c;这⾥我们主要关注x264_init。对于x264的参数都在 ffmpeg\libavcodec \libx264.c x264\co…

深度学习和神经网络 卷积神经网络CNN

1.什么是卷积神经网络 一种前馈神经网络&#xff1b;受生物学感受野的机制提出专门处理网格结构数据的深度学习模型 核心特点&#xff1a;通过卷积操作自动提取空间局部特征&#xff08;如纹理、边缘&#xff09;&#xff0c;显著降低参数量 2.CNN的三个结构特征 局部连接&a…

论文略读:LIMO: Less is More for Reasoning

202502 arxiv 在数学推理领域&#xff0c;论文提出的LIMO仅用 817 条精心设计的训练样本&#xff0c;借助简单的监督微调&#xff0c;就全面超越了使用十万量级数据训练的主流模型 最近的大模型在预训练阶段已纳入海量数学知识&#xff08;比如Llama 3 仅在数学推理上的训练数…

web架构3------(nginx的return跳转,gzip压缩,目录浏览,访问控制和location符号优先级)

一.前言 本期继续来介绍nginx的各项配置&#xff0c;看着内容很多&#xff0c;但是主要还是介绍&#xff0c;内容还是很少的。 二.return和rewrite跳转 在我们配置ssl证书之后&#xff0c;如果把https的s去掉&#xff0c;就相当于去访问80端口了&#xff0c;https默认找的是…

大楼智能化建设设计方案(Word)

第一章 智能化设计 4 1.1 项目概况 4 1.2 设计原则 4 1.3 设计依据 6 1.4 项目总体规划 7 1.5 综合布线系统 8 1.5.1 综合布线系统 8 1.5.2 楼宇分机房系统 20 1.5.3 有线电视网 27 1.6 建筑智能化系统 37 1.6.1 周界防范系统 37 1.6.2 电子巡更系统 38 1.6.3…

Spring AI 之检索增强生成(Retrieval Augmented Generation)

检索增强生成&#xff08;RAG&#xff09;是一种技术&#xff0c;有助于克服大型语言模型在处理长篇内容、事实准确性和上下文感知方面的局限性。 Spring AI 通过提供模块化架构来支持 RAG&#xff0c;该架构允许自行构建自定义的 RAG 流程&#xff0c;或者使用 Advisor API 提…

【C++/Linux】TinyWebServer前置知识之IP协议详解

目录 IPv4地址 分类 IP数据报分片 IP 协议在传输数据报时&#xff0c;将数据报分为若干分片&#xff08;小数据报&#xff09;后进行传输&#xff0c;并在目的系统中进行重组&#xff0c;这一过程称为分片&#xff08;Fragmentation&#xff09;。 IP模块工作流程​编辑 I…

破局软件开发困境:一套‘一模到底‘的功能模型,如何撬动软件工程全数字化管控?

软件工程如同一场复杂的交响乐&#xff0c;功能模型是乐谱的主旋律&#xff0c;而需求、设计、开发、测试、运维、用户反馈、Bug、版本、状态等则是丰富的配器和节奏。传统模式下&#xff0c;这些元素常常各自为营&#xff0c;声部混乱&#xff0c;难以奏出和谐的乐章。如何才能…