C++_String增删查改模拟实现
C++_String增删查改模拟实现
- 前言
- 一、string默认构造、析构函数、拷贝构造、赋值重载
- 1.1 默认构造
- 1.2 析构函数
- 1.3 拷贝构造
- 1.4 赋值重载
- 二、迭代器和范围for
- 三、元素相关:operator[ ]
- 四、容量相关:size、resize、capacity、reserve
- 4.1 size、capacity
- 4.2 reserve
- 4.3 resize
- 五、数据相关:push_bach、append、operator+=、insert、erase
- 5.1 尾插:push_back
- 5.2 append尾部插入字符串
- 5.3 operator+=()字符、字符串
- 5.4 insert插入字符、字符串
- 5.4.1 insert插入字符(在这提醒下,博主是所有的拷贝数据都是从'\0'开始,这样就不需要单独对'\0'做处理)
- 初学者最容易范的一个错误
- 5.4.2 insert插入字符串
- 5.5 erase
- 六、 关系操作符重载:< 、 ==、 <=、 >、>=、!=
- 七、find查找字符、字符串、substr
- 7.1 find查找字符
- 7.2 find查找字符串
- 7.3 strsub( ) 模拟实现
- 八、流插入和流提取(<<、>>)(实现在string类外)
- 8.1 流插入<<
- 8.1 流提取>>
- 优化
- 九、所有代码
前言
本篇博客仅仅实现存储字符的string。同时由于C++string库设计的不合理,博主仅实现一些最常见的增删查改接口!
接下来给出的接口都是基于以下框架:
namespace achieveString
{class string{private:char* _str;size_t _capacity;size_t _size;};
}
一、string默认构造、析构函数、拷贝构造、赋值重载
1.1 默认构造
博主在这仅仅提供如无参和带参默认构造接口:
//无参默认构造
string():_str(new char[1]{'\0'}),_capacity(0),_size(0)
{ }
//带参默认构造
string(const char* str = ""):_capacity(strlen(str)),_size(_capacity)
{_str = new char[_capacity + 1];strcpy(_str, str);
}
小tips:
- C++string标准库中,无参构造并不是空间为0,直接置为空指针。而是开一个字节,并存放‘\0’。(C++中支持无参构造一个对象后,直接在后面插入数据,也从侧面说明了这点)
- 由于C++构造函数不管写不写都会走初始化列表的特性,所以这里博主也走初始化列表。
- string中,_capacity和_size都不包含空指针,所以带参构造要多开一个空间,用来存储’\0’。
1.2 析构函数
~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}
1.3 拷贝构造
传统写法:
string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}
现代写法:现代写法的核心在于:将拷贝数据的工作交给别人来做,最后将成果交换一样即可。
//交换
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);
}
tips:现代写法中,拷贝构造是数据需初始化为空。原因在于C++中,编译器对内置类型不会做处理(个别如vs2019等编译器会做处理的),这也就意味这_str是一个随机值,指向任意一块空间。调用析构函数时会报错。
1.4 赋值重载
赋值重载同样分为传统写法和现代写法。
传统写法:
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;
}
现代写法:
//现代写法
//法一
/*string& operator=(const string& s)
{if (this != &s){string tmp(s._str);swap(tmp);}return *this;
}*///法二
string& operator=(string tmp)
{swap(tmp);return *this;
}
二、迭代器和范围for
在C++中,范围for在底层是通过迭代器来实现的。所以只要实现了迭代器,就支持范围for。
而迭代器类似于指针,迭代器可以被看作是指针的一种泛化,它提供了类似指针的功能,可以进行解引用操作、指针运算等。
以下提供了const迭代器和非const迭代器:
typedef char* iterator;
const typedef 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;}
三、元素相关:operator[ ]
这里我们和库中一样,提供以下两个版本
//可读可写
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、resize、capacity、reserve
4.1 size、capacity
size_t size()const
{return _size;
}
size_t capacity()const
{return _capacity;
}
4.2 reserve
在C++中,我们一般不缩容。
所以实现reserve时(容量调整到n),首先判断目标容量n是否大于当前容量。如果小于就不做处理,否则先开辟n+1个内存空间(多出来的一个用于存储‘\0’),然后将原有数据拷贝到新空间(strcpy会将’\0’一并拷贝过去)。然后释放就空间,并让_str指向新空间,同时更新_capacity。
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}
4.3 resize
resize到目标大小分为以下3中情况:
- 当n<_size时,只需将下标为n的地址处的数据改为’\0’。
- 其他情况,我们直接统一处理。直接复用reserve()函数将_capacity扩到n。然后用将[_size, n)中的数据全部初始化为ch。(这里博主给ch一个初始值’\0’,但ch不一定为’\0’,所以要将下标为n处的地址初始化为’\0’)
void resize(size_t n, char ch='\0')
{if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);while (_size < n){_str[_size] = ch;_size++;}_str[_size] = '\0';}
}
五、数据相关:push_bach、append、operator+=、insert、erase
5.1 尾插:push_back
尾插首先检查扩容,在插入数据
void push_back(char ch)
{//扩容if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//插入数据_str[_size] = ch;_size++;_str[_size] = '\0';}
5.2 append尾部插入字符串
void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity)//扩容{reserve(_size + len);}strcpy(_str + _size, str);_size += len;
}
5.3 operator+=()字符、字符串
operator+=()字符、字符串可以直接复用push_back和append。
string& operator+=(char ch)
{push_back(ch);return *this;
}string& operator+=(const char* str)
{append(str);return *this;
}
5.4 insert插入字符、字符串
5.4.1 insert插入字符(在这提醒下,博主是所有的拷贝数据都是从’\0’开始,这样就不需要单独对’\0’做处理)
insert插入字符逻辑上还是很简单的。
首先判断插入字符时是否需要扩容。然后从下标为pos开始,所有数据依次往后挪动。最后将待插入字符给到pos处即可。
初学者最容易范的一个错误
但对于初学者来说,貌似也不太轻松。。。。。。
下面给出各位初学者容易犯的错误:
void insert(size_t pos, char ch)
{assert(pos <= _size);//扩容if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//挪动数据size_t end = _size;while (end >= pos){_str[end+1] = _str[end];end--;}_str[pos] = ch;_size++
}
这样对吗?答案是错误的。
假设是在头插字符,end理论上和pos(即0)比较完后就减到-1,在下一次循环条件比较时失败,退出循环。
遗憾的是end是size_t类型,始终>=0, 会导致死循环。
博主在这给出两种解决方法:
- 将pos强转为整型。
void insert(size_t pos, char ch)
{assert(pos <= _size);//扩容if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//挪动数据int end = _size;while (end >= (int)pos){_str[end+1] = _str[end];end--;}_str[pos] = ch;_size++
}
2.从end从最后数据的后一位开始,每次将前一个数据移到当前位置。最后条件判断就转化为end>pos,不会出现死循环这种情况。
void insert(size_t pos, char ch)
{assert(pos <= _size);//扩容if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//挪动数据size_t end = _size+1;while (end > pos){_str[end] = _str[end-1];end--;}//插入数据,更新_size_str[pos] = ch;_size++;
}
5.4.2 insert插入字符串
insert同样存在相同问题,并且思路一样。博主就直接给出代码了。
法一:
void insert(size_t pos, const char* str)
{int len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}int end = _size;while (end >= (int)pos){_str[end + len] = _str[end];end--;}strncpy(_str + pos, str, len);_size += len;
}
法二:
void insert(size_t pos, const char* str)
{int len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size+1;while (end > pos){_str[end + len-1] = _str[end-1];end--;}strncpy(_str + pos, str, len);_size += len;
}
5.5 erase
erase分两种情况:
- 从pos开始,要删的数据个数超过的字符串,即将pos后序所有数据全部情况。(直接将pos处数据置为’\0’即可)
- 从pos开始,要删的数据个数没有超出的字符串。所以只需要从pos+len位置后的所有数据向前移动从pos位置覆盖原数据即可。
void erase(size_t pos, size_t len = npos)
{if (len==npos || pos + len >= _size){//有多少,删多少_str[pos] = '\0';_size = pos;}else{size_t begin = pos + len;while (begin <= _size){_str[begin - len] = _str[begin];begin++;}_size -= len;}
}
六、 关系操作符重载:< 、 ==、 <=、 >、>=、!=
bool operator<(const string& s) const
{return strcmp(_str, s._str) < 0;
}bool operator==(const string& s) const
{return strcmp(_str, s._str) == 0;
}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);
}
七、find查找字符、字符串、substr
7.1 find查找字符
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;
}
7.2 find查找字符串
size_t find(const char* sub, size_t pos = 0)
{const char* p = strstr(_str + pos, sub);if (p){return p - _str;}else{return npos;}
}
7.3 strsub( ) 模拟实现
strsub目标长度可能越界string,也可能还有没有。但不管是那种情况,最后都需要拷贝数据。所以这里我们可以先将len真实长度计算出来,在拷贝数据。
string substr(size_t pos, size_t len = npos)const
{string s;size_t end = pos + len;//目标字符越界string,更新lenif (len == npos || end >= _size){len = _size - pos;end = _size;}//拷贝数据s.reserve(len);for (size_t i = pos; i < end; i++){s += _str[i];}return s;
}
八、流插入和流提取(<<、>>)(实现在string类外)
8.1 流插入<<
由于前面我们实现了迭代器,所以最简单的方式就是范围for
ostream& operator<<(ostream& out, const string& s)
{/*for (size_t i = 0; i < s.size(); i++){out << s[i];}*/for (auto ch : s)out << ch;return out;
}
8.1 流提取>>
流提取比较特殊。在流提取前需要将原有数据全部清空。同时由于>>无法获取空字符和换行符()(都是作为多个值之间的间隔),直接流提取到ostream对象中,没法结束。(类似于C语言中scanf, 换行符和空字符仅仅只是起到判断结束的作用,但scanf无法获取到它们)
所以这里博主直接调用istream对象中的get()函数。(类似于C语言中的getchar()函数)
get详细文档
class string
{void clear(){_str[0] = '\0';_size = 0;}
private:char* _str;size_t _capacity;size_t _size;
};istream& operator>>(istream& in, string& s){s.clear();char ch;//in >> ch;ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;//in >> ch;ch = in.get();}return in;}
上面这种方法虽然可以达到目的。但还有一个问题,每次插入数据都面临可扩容问题。那如何优化呢?
优化
其中一种办法就是调用reserve()提前开好空间,但这样面临这另一个问题:开大了浪费空间;开小了,同样面临这扩容的问题。
所以在这博主采用和vs底层实现的思路:首先开好一段数组(包含’\0’,以16为例)。当数据个数小于16时,字符串存在数组中;当数据个数大于等于16时,将数据存在_str指向的空间。
这是一种以空间换时间的思路,同时也能很好的减少内存碎片的问题。
class string
{void clear(){_str[0] = '\0';_size = 0;}
private:char* _str;size_t _capacity;size_t _size;
};istream& operator>>(istream& in, string& s)
{s.clear();char buff[16];size_t i = 0;char ch;ch = in.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 16){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in;
}
九、所有代码
namespace achieveString
{class string{public:typedef char* iterator;const typedef 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():_str(new char[1]{'\0'}),_capacity(0),_size(0){ }*/string(const char* str = ""):_capacity(strlen(str)), _size(_capacity){_str = new char[_capacity + 1];strcpy(_str, str);}const char* c_str() const{return _str;}//析构函数~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}//拷贝构造/*string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}*///交换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=(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;}*///现代写法//法一/*string& operator=(const string& s){if (this != &s){string tmp(s._str);swap(tmp);}return *this;}*///法二string& operator=(string tmp){swap(tmp);return *this;}//可读可写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;}bool empty()const{return _size == 0;}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void resize(size_t n, char ch='\0'){if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);while (_size < n){_str[_size] = ch;_size++;}_str[_size] = '\0';}}void push_back(char ch){//扩容if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//插入数据_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_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);//扩容if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//挪动数据size_t end = _size+1;while (end > pos){_str[end] = _str[end-1];end--;}//插入数据,更新_size_str[pos] = ch;_size++;}void insert(size_t pos, const char* str){int len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}//法一/*int end = _size;while (end >= (int)pos){_str[end + len] = _str[end];end--;}strncpy(_str + pos, str, len);_size += len;*///法二size_t end = _size+1;while (end > pos){_str[end + len-1] = _str[end-1];end--;}strncpy(_str + pos, str, len);_size += len;}void erase(size_t pos, size_t len = npos){if (len==npos || pos + len >= _size){//有多少,删多少_str[pos] = '\0';_size = pos;}else{size_t begin = pos + len;while (begin <= _size){_str[begin - len] = _str[begin];begin++;}_size -= len;}}bool operator<(const string& s)const{return strcmp(_str, s._str) < 0;}bool operator==(const string& s)const{return strcmp(_str, s._str) == 0;}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);}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* sub, size_t pos = 0){const char* p = strstr(_str + pos, sub);if (p){return p - _str;}else{return npos;}}string substr(size_t pos, size_t len = npos){string s;size_t end = pos + len;if (len == npos || end >= _size){len = _size - pos;end = _size;}s.reserve(len);for (size_t i = pos; i < end; i++){s += _str[i];}return s;}void clear(){_str[0] = '\0';_size = 0;}private:char* _str;size_t _capacity;size_t _size;//const static size_t npos = -1; // C++支持const整型静态变量在声明时给值初始化,但不建议//const static double npos = 1.1; // 不支持const static size_t npos;};const size_t string::npos = -1;ostream& operator<<(ostream& out, const string& s){/*for (size_t i = 0; i < s.size(); i++){out << s[i];}*/for (auto ch : s)out << ch;return out;}//istream& operator>>(istream& in, string& s)//{// s.clear();// char ch;// //in >> ch;// ch = in.get();// while (ch != ' ' && ch != '\n')// {// s += ch;// //in >> ch;// ch = in.get();// }// return in;//}istream& operator>>(istream& in, string& s){s.clear();char buff[16];size_t i = 0;char ch;ch = in.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 16){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in;}
}
相关文章:

C++_String增删查改模拟实现
C_String增删查改模拟实现 前言一、string默认构造、析构函数、拷贝构造、赋值重载1.1 默认构造1.2 析构函数1.3 拷贝构造1.4 赋值重载 二、迭代器和范围for三、元素相关:operator[ ]四、容量相关:size、resize、capacity、reserve4.1 size、capacity4.2…...

LeeCode前端算法基础100题(2)- 最多水的容器
一、问题详情: 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明:…...

排序算法--归并排序
实现逻辑 ① 将序列每相邻两个数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素 ② 将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素 ③ 重复步骤②,直到所有元素排序完毕 void pri…...

【LeetCode:1410. HTML 实体解析器 | 模拟+哈希表+字符串+库函数】
🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…...

基于SSM的公司仓库管理系统(有报告)。Javaee项目
演示视频: 基于SSM的公司仓库管理系统(有报告)。Javaee项目 项目介绍: 采用M(model)V(view)C(controller)三层体系结构,通过Spring SpringMvc …...

spark数据倾斜的解决思路
数据倾斜是:多个分区中,某个分区的数据比其他分区的数据多的多 数据倾斜导致的问题: 导致某个spark任务耗时较长,导致整个任务耗时增加,甚至出现OOM运行速度慢:主要发生在shuffle阶段,同样的k…...

Python武器库开发-前端篇之html概述(二十八)
前端篇之html概述(二十八) html概述 HTML5是构建Web内容的一种语言描述方式。HTML5是互联网的下一代标准,是构建以及呈现互联网内容的一种语言方式.被认为是互联网的核心技术之一。HTML产生于1990年,1997年HTML4成为互联网标准,…...

安防视频EasyCVR平台太阳能供电+4G摄像头视频监控方案的建设
在工地、光伏、风电站、水库河道等场景中,以及一些偏远地区的项目现场,会存在无网无电情况,大大制约了视频监控系统建设的效率及可行性。在这种场景中,我们也可以通过太阳能供电4G监控摄像机的方案,满足偏远地区无网无…...
12.位运算的性质(异或的性质)
文章目录 异或的性质求异或和问题[421. 数组中两个数的最大异或值](https://leetcode.cn/problems/maximum-xor-of-two-numbers-in-an-array/)[2935. 找出强数对的最大异或值 II](https://leetcode.cn/problems/maximum-strong-pair-xor-ii/) 异或前缀和问题(最..回…...

国标直流充电枪9孔分别啥意思?
DC:直流电源正 DC-:直流电源负 PE:接地(搭铁)S:通讯CAN-H S-:通讯CAN-L CC1:充电连接确认 CC2:充电连接确认 A:12V A-:12V- 以上就是国标直流充电…...

关于 Google AMP 和 SEO
Google 于 2015 年首次推出 AMP,即加速移动页面。借助开源 AMP 框架,网页设计师可以制作快速加载的移动网页。该框架的创建是为了应对使用移动设备访问互联网的个人数量的增加。从那时起,谷歌一直在推动使用 AMP 来增强移动设备上的 SEO 和用…...

【SpringMVC】 对请求的不同响应
前言 本文学习如何运用不同的注解来返回不同的响应. 1.返回静态页面Controller 返回index.html页面 Controller 和 RestController的区别 controller 只有加上这个注解,Spring才会帮我们管理这个代码.后续我们访问时才能访问到. RestController 等同于 Controller ResponseBo…...

SQL进阶学习
1.[NISACTF 2022]join-us sql报错注入和联合注入 过滤: as IF rand() LEFT by updatesubstring handler union floor benchmark COLUMN UPDATE & sys.schema_auto_increment_columns && 11 database case AND right CAST FLOOR left updatexml DATABA…...
邦芒解析:做好职场规划防止跳槽失败
为了防止跳槽进入不适合自己的工作环境,你可以采取以下措施: 1、做好调研:在决定跳槽之前,尽可能了解新公司的情况。这包括公司的文化、工作氛围、发展前景以及团队成员之间的关系等。通过搜索公司网站、阅读员工评价以及与公司内…...

基于springboot实现实习管理系统的设计与实现项目【项目源码+论文说明】计算机毕业设计
基于sprinmgboot实现实习管理系统的设计与实现演示 摘要 随着信息化时代的到来,管理系统都趋向于智能化、系统化,实习管理也不例外,但目前国内仍都使用人工管理,市场规模越来越大,同时信息量也越来越庞大,…...
【华为OD题库-031】比赛的冠亚季军-java
题目 有N(3<N<10000)个运动员,他们的id为0到N-1,他们的实力由一组整数表示。他们之间进行比赛,需要决出冠亚军。比赛的规则是0号和1号比赛,2号和3号比赛,以此类推,每一轮,相邻的运动员进行比赛&#…...

电脑如何禁止截屏
禁止电脑截屏是一项重要的安全措施,可以保护用户隐私和防止恶意软件的使用。以下是几种禁止电脑截屏的方法: 形式一: 一刀切,全部禁止截屏 可以在域之盾软件后,点击桌面管理,然后选择禁止截屏。就能禁止所…...

【Web】NewStarCTF Week1 个人复现
目录 ①泄露的秘密 ②Begin of Upload ③Begin of HTTP ④ErrorFlask ⑤Begin of PHP ⑥R!C!E! ⑦EasyLogin ①泄露的秘密 盲猜/robots.txt,访问得到flag前半部分 第二个没试出来,老老实实拿dirsearch扫吧 访问/www.zip 下载附件,拿到第二部分…...

Android 提示框代码 java语言
在Android中,你可以使用 AlertDialog 类来创建提示框。以下是一个简单的Java代码示例,演示如何创建和显示一个基本的提示框: import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; im…...
【c语言】二维数组的对角线对称交换
c语言,假设已经有了一个二维数组,对其进行对角线对称变换,如(0,1)与(1,0)变换,并打印。 示例 #include <stdio.h>void swap(int *a, int *b) {int te…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...

高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...

aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...