c++STL-string的模拟实现
c++STL-string的模拟实现
- string的模拟实现
- string的模拟线性表的实现
- 构造函数
- 析构函数
- 获取长度(size)和获取容量(capacity)
- 访问 [] 和c_str
- 迭代器(iterator)
- 交换swap
- 拷贝构造函数
- 赋值重载(=)
- 扩容(reserve和resize)
- 插入(insert)
- 尾插(push_back、+=、append)
- 比较运算符重载
- 删除(erase)
- 清空(clear)
- 流插入(operator<<)和流提取(operator>>)
- 查找(find)
- 取字符串(substr)
- string模拟参考程序
string的模拟实现
建议先看c++STL-string的使用-CSDN博客
这里的模拟实现是帮助自己更好地理解string
。不排除以后觉得库里的string
不好用,自己亲自写一个更好的。
并且c++的标准并没有规定怎么实现,而是给定了指标,程序员达到这个标准即可。
这里贴一个功能文档,后期自己想复习了,就照着这个文档搓一遍string
。
string的模拟线性表的实现
这里参考顺序表的概念和实现-CSDN博客:
class string{
public://const static size_t npos=-1;//特例,不建议用特例const static size_t npos;
private:char* _str;size_t _size;size_t _capacity;
}
const size_t string::npos=-1;
_size
表示\0
的位置。
_capacity
表示数组的大小。数组需要预留1个空间给\0
,因此_capacity
会比实际情况小1。
const
静态整型变量可以给缺省值。类的成员变量给的缺省值是给初始化列表使用,但静态成员变量不会通过构造函数的初始化列表,所以按照标准应该是不可以的。猜测写c++语法的人或写编译器的人为了个人方便,为
const
静态整型变量开了一个特例,甚至只能是整型,别的类型比如double
不允许。
因为string
的成员函数很多而且功能大都是重复的,所以只选几个重要的功能模拟实现。
也不做basic_string
的类模板,就只做basic_string<char>
。
不确定的功能也可以用库中的作对比。
构造函数
对比string
的构造函数接口:
这里选择形参为char*
的构造函数来模拟实现。
#include<iostream>
#include<string>
using namespace std;int main() {string st;cout << st;//调用默认构造函数也不会越界string st2("abc");cout << st2;return 0;
}
所以构造函数满足不给初始值也不会越界(即至少有1个\0
)。
目前能想到的方案是构造函数的形参给全缺省,用全缺省的形参去初始化成员变量,用全缺省的形参也能省去无参构造函数的定义。
几种常见的不合理的构造函数:
//1
//成员指针_str可修改,str经过const修饰不可通过str修改内存
//_str也不能加const修饰。
string(const char* str):_str(str)//,_size(strlen(str))
{//拷贝字符串
}//2
//初始化列表按照成员变量在类中声明的顺序。
//所以_capacity
string(const char* str):_size(strlen(str)),_capacity(strlen(str)),_str(new char[_capacity+1])
{//拷贝字符串
}
析构函数
析构函数如果不在意效率,可以按照顺序表的方式去释放空间。如果在意效率,后文会介绍引用计数和写时拷贝。
获取长度(size)和获取容量(capacity)
size_t size() const;
size_t capacity() const;
获取长度:返回_size
,并保证不会修改*this
。
获取容量:返回_capacity
,并保证不会修改*this
。
这样可以方便查看对象的情况。
访问 [] 和c_str
重载operator[]
,返回类型char&
。需要注意判断下标是否合理。
要提供2个[]
,一个不加const
,另一个返回值和this
指针都加const
。
c_str
返回_str
的地址。
迭代器(iterator)
iterator begin();
const_iterator begin() const;iterator end();
const_iterator end() const;
迭代器的功能和指针类似,但不一定是指针。在模拟实现阶段用指针代替。
其中范围for
要求类至少有普通版本和const
版本的begin()
和end()
(迭代器但凡更换一个名字都不支持,例如Begin
,范围for
就不支持),并且这些迭代器能正常访问,以及解引用(*
)、前置递增(++
)以及相等比较(==
)和不等比较(!=
)。
对于内置类型的数组来说,指针本身就可以作为迭代器使用,因为指针支持这些操作。
其中迭代器使用基础的begin
和end
即可,反向迭代器需要用到适配器模式,在初学阶段暂时用不上。但可以通过其他迭代器类间接调用已有的begin
和end
,以及相关的操作来实现。
交换swap
两个string
对象完全可以只交换成员变量的值,这样可以减少多余的拷贝带来的额外开销。
拷贝构造函数
-
用另一个对象初始化
*this
,需要另外开辟空间。 -
还可以利用形参的
_str
去调用构造函数来初始化临时对象,之后*this
和临时对象交换_str
的地址即可。但需要注意的是,新生成的临时对象不会去调用原始的构造函数,因此需要额外初始化。
之后临时对象调用析构函数,和*this
没有关系。
毕竟也是构造函数,需要对成员变量进行初始化。
拷贝构造函数还会面临另外的问题:浅拷贝问题。
浅拷贝问题是因为类的默认成员函数会自动运行做一些可能出问题的事(比如两个对象中的指针同时指向堆区的某块内存,连续进行两次释放的行为)。如果能花费较小的代价阻止默认成员函数的部分行为,则可以让浅拷贝成为一种优化方式。详细见引用计数和写时拷贝。
赋值重载(=)
char*
可以通过调用构造函数间接实现,因此只需要再实现一个char
为形参的即可。
进行赋值重载前先进行容量检测(reserve
)。
-
先进行判断,防止自己给自己赋值做多余的工作。
后检查容量,容量不够时需要扩容,否则直接拷贝即可。
-
还可以利用形参的
_str
去调用构造函数来初始化临时对象,之后*this
和临时对象交换_str
的地址即可。 -
甚至还可以用传值传参,系统自己调用拷贝构造函数,再交换数据即可。
赋值重载也会涉及浅拷贝问题。详细见引用计数和写时拷贝
三种选择都可以,更推荐后面两个。
扩容(reserve和resize)
string
的操作中,增加字符串中的内容需要扩容。因此推荐先实现扩容。
首先实现reserve
。检测容量是否足够,不够的话扩容,size
不变。同时注意初始capacity
。
如果是缩容,reserve
什么也不做。
c++尽量不用
realloc
扩容,而是另外new
另一个空间,因为要扩容的空间可能是自定义类型,需要调用其他类的构造函数和析构函数。
如果是resize
:
_size
小于要扩容的大小,则在reserve
的基础上更改_size
的值,并用指定的符号去填充即可。_size
大于要扩容的大小,则相当于缩容,只需要更改_size
的值并赋值\0
即可。
根据实际经验,MSVC和mingw64的扩容方式不同(MSVC是1.5倍,mingw64是2倍扩容)。
用哪个都可以。
扩容时统一使用新开空间。虽然可以使用realloc
,但很多时候realloc
也是开辟新空间,释放旧空间。
插入(insert)
插入函数选择形参是(size_t,string&)
的实现即可。
首先先检查容量是否支持插入新的字符。
然后下标pos
后的字符串整体后移若干个单位,并在pos
处插入一个字符(串)。可能涉及扩容。
而且需要特别注意头插和尾插的情况。若用于表示下标的整型用的是无符号(size_t
),则要注意在无符号数的情况下负数的情况。特别是 -1 反而是最大值的情况。
尾插(push_back、+=、append)
因为原版的string
,append
的设计和+=
的功能重复。因此仅实现append
和+=
其中之一,另一个用代码复用即可。
也可以只实现insert
,之后凡是和插入有关的都复用insert
即可。
往字符串结尾插入字符,如果容量不够需要扩容。push_back
是插入1个,+=
可以插入1个,也可以插入整个字符串。
扩容需要留意_capacity
是否为0。可以用c语言自带的strcpy
,将\0
顺带拷贝回去。
而且有必要的话需要提供形参为const
的另一个版本的成员函数。
比较运算符重载
比较运算符重载有两个操作数,可以做成全局函数,将这些比较运算符设置为友元,或直接通过operator[]
访问。
可以用c语言的库函数strcmp
代替。但为了增强代码的独立性,尽量选择尽量不使用c
语言自带的库函数。
而且在这个地方函数复用会很多,需要注意形参的权限。比如const
修饰的形参不可作为实参上传给非const
修饰的形参的函数(权限放大)。
删除(erase)
string& erase(size_t pos = 0, size_t len = npos);
从下标pos
开始删除长度为len
的字符。
若pos>=_size
则应阻止删除。
若_size<=pos+len
(或len==npos
),则_str[pos]='\0'
,更新_size
即可。
若_size>pos+len
,则需要挪动数据并更新size
。
清空(clear)
将首字符设置为空字符\0
并修改_size;
为0即可。
可以释放空间,但没必要。
流插入(operator<<)和流提取(operator>>)
这里的流插入不一定要写成友元。string
还可以通过访问operator[]
来访问数据。
若流插入的形参用const
修饰,则流插入调用的一些相关的函数比如迭代器也需要用const
修饰this
。
流提取的形参不能用const
修饰,因为流提取需要修改后台的数据。
输入的内容通过流提取尾插到string
对象中,需要注意的是系统自带的流提取(cin
和operator
)默认将空格和换行作为不同数据的分界,所以需要将所有的字符包括空格和换行符也读取。
读取所有字符的方法:
scanf("%c",&st[i]);
、istream::get()
、getchar()
。既然选择了c++,所以更推荐用
istream::get
。
在读取之前先将string
对象清空(clear)。
流提取还可以用申请在函数作用域内的数组优化,将数组通过operator+=
尾插给对象。这样做可以减少扩容次数。
查找(find)
实现的时候可以用暴力查找,时间复杂度为 O ( n × m ) O(n\times m) O(n×m)。如果觉得找匹配串的效率太低还可以尝试kmp算法(理论效率很高,实际效率看情况)或字符串BM算法。
如果是从某个位置开始找,则用_str+pos
即可。
取字符串(substr)
string substr (size_t pos = 0, size_t len = npos)const;
因为要返回临时对象,所以需要先实现拷贝构造函数。
生成临时对象,再从*this
中取指定长度尾插给临时对象,最后返回临时对象(不能串引用返回)。
如果要取的长度为npos
或大于_size
,则一直取到尾即可。
取字符串的时候需要注意浅拷贝问题。
string模拟参考程序
模拟实现时将自己的string
放在另一个命名空间mystd
里,这样想用库里的string
就可以加域名std::
,想用自己的string
就加域名mystd::
。
最终参考程序(无反向迭代器)的某 .h 文件:
#pragma once
#include<cassert>
#include<iostream>
using std::ostream;
using std::istream;namespace mystd {template<class T>void swap(T& a, T& b) {T tmp = a;a = b;b = tmp;}class string {public:const static size_t npos;//构造函数string(const char* s = "") {//求s的长度size_t len = 0;const char* tmps = s;while (*tmps) {++len;++tmps;}//初始化长度、容量等信息_size = len;_capacity = len;_str = new char[_capacity + 1]{ '\0' };//拷贝tmps = s;char* tmps2 = _str;while (*tmps2++ = *tmps++);}//析构函数~string() {delete[] _str;_str = nullptr;_size = _capacity = 0;}//返回c风格字符串的地址const char* c_str() const {return _str;}//用[]进行索引访问char& operator[](size_t pos) {assert(pos < _size);return _str[pos];}const char& operator[](size_t pos) const {assert(pos < _size);return _str[pos];}//返回当前字符串的长度size_t size()const {return _size;}//返回容量size_t capacity()const {return _capacity;}//迭代器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;}//交换,独立于namespace std中的swapvoid swap(string& a) {//两个对象:*this,amystd::swap(_str, a._str);mystd::swap(_size, a._size);mystd::swap(_capacity, a._capacity);}//拷贝构造函数string(const string& str):_str(nullptr)//临时对象不经过构造函数会产生随机数, _size(0), _capacity(0) {string tmp(str._str);//生成第2个临时对象mystd::string::swap(tmp);}//赋值重载:string& operator=(string tmp) {mystd::string::swap(tmp);return *this;}string& operator=(char c) {char ch[2] = { c,'\0' };string tmp(ch);mystd::string::swap(tmp);return *this;}//扩容void reserve(size_t n = 0) {if (n > _capacity) {char* tmp = new char[n + 1]{ '\0' };char* aa = tmp, * bb = _str;while (*aa++ = *bb++);delete[]_str;_str = tmp;_capacity = n;}}void resize(size_t n, char ch = '\0') {if (n <= _size) {_str[n] = '\0';_size = n;while (n < _capacity)_str[n++] = '\0';return;}reserve(n);while (_size < n) {_str[_size] = ch;++_size;}}//插入string& insert(size_t pos, const string& s) {assert(pos <= _size);reserve(_size + s._size);size_t end = _size - 1;while (end >= pos && end != -1) {_str[end + s._size] = _str[end];--end;}for (size_t i = 0; i < s._size; i++) {_str[pos + i] = s[i];}_size += s._size;return *this;}//尾插string& operator+=(const string& s) {insert(_size, s);_str[_size] = '\0';return *this;}string& operator+=(char s) {push_back(s);return *this;}void push_back(char c) {char ch[2] = { c,'\0' };insert(_size, ch);}//比较运算符重载bool operator==(const string& s) const {if (_size != s._size)return 0;for (size_t i = 0; i < s._size; i++) {if (_str[i] != s._str[i]) {return 0;}}return 1;}bool operator<(const string& s) const {for (size_t i = 0; i < s._size && i < _size; i++) {if (_str[i] > s._str[i]) {return 0;}}if (_size > s._size)return 0;return 1;}bool operator>(const string& s) const {return !(*this == s || *this < s);}bool operator>=(const string& s) const {return !(*this < s);}bool operator<=(const string& s) const {return !(*this > s);}bool operator!=(const string& s) const {return !(*this == s);}//删除string& erase(size_t pos = 0, size_t len = npos) {assert(pos < _size);if (len == npos || pos + len >= _size) {_str[pos] = '\0';_size = pos;return *this;}while (pos + len < _size) {_str[pos] = _str[pos + len];++pos;}_str[pos] = '\0';_size -= len;return *this;}//清空void clear() {_str[0] = '\0';_size = 0;}//查找size_t find(const string& s, size_t pos = 0)const {for (size_t i = 0, ti; i <= _size - s._size; i++) {ti = i;for (size_t j = 0; j < s._size; j++) {if (s[j] == _str[ti])++ti;elsebreak;}if (ti == i + s._size)return i;}return npos;}size_t find(char c, size_t pos = 0)const {for (size_t i = 0; i < _size; i++) {if (_str[i] == c)return i;}return npos;}//提取片段string substr(size_t pos = 0, size_t len = npos)const {string tmp;if (len == npos || pos + len > _size) {while (tmp += _str[pos++], pos < _size);return tmp;}while (tmp += _str[pos++], tmp._size < len);return tmp;}private:char* _str;size_t _size;size_t _capacity;};const size_t string::npos = -1;ostream& operator<<(ostream& out, const string& st) {for (const auto& x : st)out << x;return out;}istream& operator>>(istream& in, string& s) {s.clear();char buff[129];size_t i = 0;char ch;//连同空格一起接收while (ch = in.get(), ch != ' ' && ch != '\n') {buff[i++] = ch;if (i == 128) {buff[i] = '\0';s += buff;i = 0;}}if (i != 0) {buff[i] = '\0';s += buff;}return in;}
}
相关文章:

c++STL-string的模拟实现
cSTL-string的模拟实现 string的模拟实现string的模拟线性表的实现构造函数析构函数获取长度(size)和获取容量(capacity)访问 [] 和c_str迭代器(iterator)交换swap拷贝构造函数赋值重载(&#x…...

YashanDB(崖山数据库)V23.4 LTS 正式发布
2024年回顾 2024年11月我们受邀去深圳参与了2024国产数据库创新生态大会。在大会上崖山官方发布了23.3。这个也是和Oracle一样采用的事编年体命名。 那次大会官方希望我们这些在一直从事在一线的KOL帮助产品提一些改进建议。对于这样的想法,我们都是非常乐于合作…...

python 写一个工作 简单 番茄钟
1、图 2、需求 番茄钟(Pomodoro Technique)是一种时间管理方法,由弗朗西斯科西里洛(Francesco Cirillo)在 20 世纪 80 年代创立。“Pomodoro”在意大利语中意为“番茄”,这个名字来源于西里洛最初使用的一个…...
C++.IP协议通信
C++IP协议通信 1. TCP协议通信1.1 服务端实现创建套接字绑定地址监听连接接受连接数据传输关闭连接1.2 客户端实现创建套接字连接服务器数据传输关闭连接1.3 示例代码服务端代码示例客户端代码示例绑定地址接收数据发送数据关闭套接字2.2 客户端实现创建套接字发送数据接收数据…...
css背景相关
背景书写 background: url(src); // 注意:在写动态样式时,backgournd赋值格式错误,是不会在浏览器dom的style上显示的 // 但是可以创建不可见的img,预加载来提高性能背景也会加载图片资源 同img的src一样,background也…...

PyCharm 加载不了 conda 虚拟环境,不存在的
#工作记录 前言 在开发过程中,PyCharm 无法加载 Conda 虚拟环境是常见问题。 在不同情况下,“Conda 可执行文件路径”的指定可能会发生变化,不会一尘不变,需要灵活处置。 以下是一系列解决此问题的经验参考。 检查 Conda 安装…...

设计模式学习整理
目录 UML类图 设计模式六大原则 1.单一职责原则 2.里氏替换原则 3.依赖倒置原则 4.接口隔离原则 5.迪米特法则(最少知道原则) 6.开(放封)闭原则 设计模式分类 1.创建型模式 2.结构型模式 4.行为型模式 一、工厂模式(factory——简单工厂模式和抽象工厂模式) 1.1、…...

二分查找的理解
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h>int binary_search(int arr[], int k, int sz) {int left 0;int right sz - 1;//这个是下标,减一是因为在0开始的,怕越界(访问无效)while (left < right){int mid…...

【Java】线程实例化 线程状态 线程属性
线程实例化 继承 Thread 类 创建类继承自 Thread 类 . class MyThread extends Thread重写 run() 方法 . Overridepublic void run(){// 线程要执行的任务代码}实例化自定义线程类 . 实现 Runnable 接口 创建类实现 Runnable 接口 . class MyRunnable implements Runnable实…...

卫宁健康WiNGPT3.0与WiNEX Copilot 2.2:医疗AI创新的双轮驱动分析
引言:医疗AI的双翼时代 在医疗信息化的浪潮中,人工智能技术的深度融入正在重塑整个医疗行业。卫宁健康作为国内医疗健康和卫生领域数字化解决方案的领军企业,持续探索AI技术在医疗场景中的创新应用。2025年5月10日,在第29届中国医院信息网络大会(CHIMA2025)上,卫宁健康…...

I2C通讯
3.1. 本章节的代码仓库 1 2 3 4 5 6 #如之前有获取则可跳过 #获取仓库 git clone https://gitee.com/LubanCat/lubancat_rk_code_storage.git#代码所在的位置 lubancat_rk_code_storage/quick_start/i2c3.2. i2c I2C(Inter-Integrated Circuit)是一种通用的总线协…...

Excel实现单元格内容拼接
一、应用场景: 场景A:将多个单元格拼接,比如写测试用例时,将多个模块拼接,中间用“-”隔开 场景B:将某单元格内容插入另一单元格固定位置(例如在B1中添加A1的内容) 二、实际应用&a…...

2025前端面试遇到的问题(vue+uniapp+js+css)
Vue相关面试题 vue2和vue3的区别 一、核心架构差异 特性Vue2Vue3响应式系统基于Object.defineProperty基于Proxy(支持动态新增/删除属性)代码组织方式Options API(data/methods分块)Composition API(逻辑按功能聚合&am…...
大模型核心基础简介
大模型核心基础简介目录 一、大模型简介定义核心特征 二、大模型的发展历程1. 早期奠基(1950s–2010s)1950s–1980s:神经网络萌芽1990s–2010s:深度学习前夜 2. 架构革命:Transformer的诞生与预训练范式(20…...

广东省省考备考(第八天5.11)—言语:逻辑填空(每日一练)
错题 解析 第一空,搭配“期盼”,且根据“生命,是来自上天的馈赠”,可知父母对孩子的出生是非常期盼的。A项“望穿秋水”,形容对远地亲友的殷切盼望,C项“望眼欲穿”,形容盼望殷切,均…...

github+ Picgo+typora
github Picgotypora 本文将介绍如何使用Picgo在typora中实现上传服务 创建github仓库以及配置token 创建仓库 注意需要Initialize 添加README 配置为public 配置token github点击头像找到setting 选择Developer setting 配置token generate 选第一个第二个都行(我这里选第…...

[网安工具] IP 信息收集工具 —— LBD · 使用手册
🌟想了解其它网安工具?看看这个:[网安工具] 网络安全工具管理 —— 工具仓库 管理手册 lbd | Kali Linux ToolsVideolbd Usage ExampleTest to see if the target domain (example.com) is using a load balancer:rootkali:~# lbd example.c…...
本贴会成为记录贴
这几天有些心力交瘁了 一方面带着对互联网下行的伤心,一方面是对未来的担忧 一转眼好像就是20 21那个 可以在宿舍肆意玩手机 大学生活 可是我不小了 是个26岁的人了 时间很快 快的就好像和自己开了一个玩笑 我以为可以找到一个自己足够喜欢的 可爱的人 可是我没有 …...

说说es配置项的动态静态之分和集群配置更新API
这天因为某件工作来到了es官网某个参数配置相关的页面,注意到了下图圆圈里的“Dynamic”: 链接:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/modules-cluster.html#misc-cluster-settings 显然这是对配置项的一个描述&am…...

LLMs之Mistral Medium 3:Mistral Medium 3的简介、安装和使用方法、案例应用之详细攻略
LLMs之Mistral Medium 3:Mistral Medium 3的简介、安装和使用方法、案例应用之详细攻略 目录 Mistral Medium 3 简介 1、Mistral Medium 3 特点 Mistral Medium 3 安装和使用方法 2、使用方法 (1)、创建Agent (2)、模型微调 Mistral Medium 3 案例应用 Mistr…...

并发设计模式实战系列(17):信号量(Semaphore)
🌟 大家好,我是摘星! 🌟 今天为大家带来的是并发设计模式实战系列,第十七章信号量(Semaphore),废话不多说直接开始~ 目录 一、核心原理深度拆解 1. 信号量本质模型 2. 并发控制…...

RAGMCP基本原理说明和相关问题解惑
一、RAG架构原理和局限性 1.1 概念解释 RAG(Retrieval-Augmented Generation):检索增强生成,让大模型接受外部输入后,总结输出 向量数据库:向量数据通常是高维空间中的点,代表复杂的数据结构…...

Java学习手册:服务注册与发现
一、服务注册与发现的概念 在微服务架构中,服务注册与发现是核心功能之一。由于微服务架构中服务实例的数量和位置是动态变化的,服务注册与发现机制允许服务实例在启动时自动注册到注册中心,并在停止时自动注销。其他服务可以通过查询注册中…...
双向Transformer:BERT(Bidirectional Encoder Representations from Transformers)
基于Transformer架构,通过双向上下文建模训练,提高完成任务的性能。 一 BERT的核心理念 1.1双向上下文建模依赖 之前讲的双向递归是用两个RNN进行,而BERT是通过Transformer的自注意力机制同时捕捉上下文信息。 1.1.1掩码语言模型…...

EdgeOne Pages MCP 入门教程
什么是MCP? MCP (Model Context Protocol) 是一个开放协议,允许 AI 模型安全地与本地和远程资源进行交互。通过在支持 MCP 的客户端(如 Cline、Cursor、Claude 等)上进行统一配置,可以让 AI 访问更多资源并使用更多工…...

Maven 公司内部私服中央仓库搭建 局域网仓库 资源共享 依赖包构建共享
介绍 公司内部私服搭建通常是为了更好地管理公司内部的依赖包和构建过程,避免直接使用外部 Maven 中央仓库。通过搭建私服,团队能够控制依赖的版本、提高构建速度并增强安全性。公司开发的一些公共工具库更换的提供给内部使用。 私服是一种特殊的远程仓…...

1688代采系统:技术架构与应用实践
在电商领域,1688 作为国内领先的 B2B 电商平台,拥有海量的商品信息。这些数据对于企业采购决策、市场分析、价格监控和供应链管理具有重要价值。本文将详细介绍如何使用 Python 爬虫技术,通过 1688 的商品详情接口(item_search 和…...

一种混沌驱动的后门攻击检测指标
摘要 人工智能(AI)模型在各个领域的进步和应用已经改变了我们与技术互动的方式。然而,必须认识到,虽然人工智能模型带来了显著的进步,但它们也存在固有的挑战,例如容易受到对抗性攻击。目前的工作提出了一…...

【2025最新】为什么用ElasticSearch?和传统数据库MySQL与什么区别?
Elasticsearch 深度解析:从原理到实践 一、为什么选择 Elasticsearch? 数据模型 Elasticsearch 是基于文档的搜索引擎,它使用 JSON 文档来存储数据。在 Elasticsearch 中,相关的数据通常存储在同一个文档中,而不是分散…...

c++的模板和泛型编程
c的模板和泛型编程 泛型编程函数模板函数模板和模板函数函数模板的原理函数模板的隐式、显式实例化模板参数的匹配原则 类模板类模板的实例化模板的使用案例用函数模板运行不同的模板类用函数模板运行不同的STL容器 模板的缺省参数非类型模板参数模板的特化函数模板的特化类模板…...