一、STL的定义

        STL是C++标准库的一部分它不仅是一个可复用的组件库还是一个包含数据结构和算法的软件框架。

二、STL的历史和版本

原始版本:

        Alexander Stepanov、Meng Lee在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。HP 版本--所有STL实现版本的始祖。

P.J.版本:

        由P.J.Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。

RW版本:

        由Rouge Wage公司开发,继承自HP版本,被C++Builder采用,不能公开或修改,可读性一般。

SGI版本:

        由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和编程风格上看,阅读性非常高。

三、STL的六大组件

STL的六大组件:仿函数、空间配置器、算法、容器、迭代器、配接器

四、STL的重要性

 1、笔试中不用自己写二叉树和栈、队列等等快速解答。

 2、帮助我们应对面试中HR的提问

 3、不懂STL不要说你会C++,STL在工作中可以帮助我们不要自己写数据结构和算法,让我们快速开发。

五、学习STL的方法

        《The C++ Standard Library》一书中把学习STL比喻成三个境界:熟用STL,了解STL的底层、扩展STL

六、string类

string类文档链接:string - C++ Reference 

        string是char类型的顺序表的类同时也STL的一种容器,使用string类的时候需要包含头文件和using namespace std;

        我们要想学习string类先了解它的构造函数和接口。

 1、string类的构造函数(string::string - C++ Reference)

注意:由于析构函数程序结束之后就自动调用所以我们不需要太关注析构函数。

       注意:我们只要掌握常见的几种构造就行:

  1.1、string()

代码示例:

#include<string>
#include<iostream>
using namespace std;int main()
{string s1;//调用默认构造cout << s1 << endl;return 0;
}

注意:流插入和流提取已经在库里面重载了,我们直接用就行。 

运行结果:

 注意:从上面运行结果可以看出调用默认构造编译器啥也不干。

        1.2、string(const char* s)

代码示例1:

#include<string>
#include<iostream>
using namespace std;int main()
{string s2("hello word");cout << s2 << endl;return 0;
}

运行结果:

   

 代码示例2:

#include<string>
#include<iostream>
using namespace std;int main()
{//string s2("hello word");string s3 = "hello word";//隐式类型转换cout << s3 << endl;return 0;
}

运行结果:

         1.3、string(const string& str)

代码示例:

#include<string>
#include<iostream>
using namespace std;int main()
{string s1("hello world");string s2(s1);//拷贝构造cout << s2 << endl;return 0;
}

运行结果:

        1.4、string(size_t n,char c) 

代码示例:

#include<string>
#include<iostream>
using namespace std;int main()
{string s1("hello world");string s2(10,'I');//用10I来构造s2cout << s2 << endl;return 0;
}

运行结果:

 2、string的赋值重载(string::operator= - C++ Reference)

他们用法都差不多这里就不一一细讲。

代码示例:

#include<string>
#include<iostream>
using namespace std;int main()
{string s3 = "HooL";cout << s3 << endl;return 0;
}

运行结果:

  

3、string的遍历和修改(https://legacy.cplusplus.com/reference/string/string/operator[]/)

3.1下标+[  ]遍历  

代码示例:

#include<string>
#include<iostream>
using namespace std;int main()
{string s1("hello world");cout << s1 << endl;s1[0] = 'x';cout << s1 << endl;s1[1]++;cout << s1 << endl;return 0;
}

运行结果: 

#include<string>
#include<iostream>
using namespace std;int main()
{string s1("hello world");for (int i = 0; i < 11; i++){cout << s1[i];}return 0;
}

 运行结果:

小贴士:修改和遍历本质是运算符的重载。

注意:string的size是不包含\0,如:

#include<string>
#include<iostream>
using namespace std;int main()
{string s1("hello world");cout << s1.size();return 0;
}

运行结果:

注意:string会对[ ]进行检查看它是否越界,如果越界会断言报错。如:

代码示例:

#include<string>
#include<iostream>
using namespace std;int main()
{string s1("hello world");s1[12];return 0;
}

运行结果:

3.2、迭代器遍历和修改

        迭代器跟指针差不多,那么我们来看看怎么用迭代器来遍历string。

代码示例:

int main()
{string s1("hello world");string::iterator it = s1.begin();//begin指向第一个字符while (it != s1.end())//end指向\0字符{cout << *it ;it++;}return 0;
}

 运行结果:

int main()
{string s1("hello world");string::iterator it = s1.begin();//begin指向第一个字符while (it != s1.end())//end指向\0字符{*it = 'h';cout << *it ;it++;}return 0;
}

运行结果:

注意:下标+[  ] 和迭代器访问和修改的区别,下标+[  ] 是特定容器支持,二迭代器访问是容器的通用访问和修改方式

3.3、范围for遍历

代码示例1:

int main()
{int i = 10;auto k = i;//根据i的类型推出k的类型,相当于:int k=i;auto j = &i;//相当于:int* j=&i;auto* a = &i;//相当于int* a=&iauto* b = i;//相当于:int* b=i;//编译报错auto c = i;//相当于:int& c=i;return 0;
}

代码示例2: 

auto Add(auto x, auto y)
{return x + y;
}int main()
{int x = 1, y = 2;cout << Add(x, y);return 0;
}

运行结果:

注意:只有C++20以上的版本才支持这样写,atuo尽量少用。

代码示例3

int main()
{string s1("hello world");for (auto e : s1)//自动取容器的数据依次给对象,自动判断结束{cout << e;}return 0;
}

运行结果:

代码示例4:

int main()
{string s1("hello world");for (auto e : s1)//把s1的元素一一给e,e就是s1的拷贝,e的修改和不影响s1{e--;cout << e;}cout << endl;cout << s1;return 0;
}

 运行结果:

代码示例5:

int main()
{string s1("hello world");for (auto& e : s1)//如果想修改s1的值可以用引用{e--;cout << e;}cout << endl;cout << s1;return 0;
}

 运行结果:

 3.4、string迭代器介绍

这里只介绍 begin 和 end 因为后面的迭代器跟这两个差不多。

begin()和end()是个迭代器,迭代器我们理解为是一个指针。

 用法示例:

int main()
{string s("hello world");string::iterator it = s.begin();while (it != s.end()){cout << *it;//可以像指针那样访问it++;}return 0;
}

注意:begin()指向起始位置,end()指向\0

那么 rbegin() 和 rend() 的用法是:

 

int main()
{string s("hello world");string::reverse_iterator rit = s.rbegin();//rend()指向第一个数据的前一个,rbegin()指向最后一个数据不包含\0while (rit != s.rend()){cout << *rit;//可以像指针那样访问rit++;//和正向迭代器不一样,反向迭代器++往左走}return 0;
}
int main()
{string s("hello world");//反向迭代器和正向迭代器的类型不一样string::reverse_iterator rit = s.rend();//rend()指向第一个数据的前一个,rbegin()指向最后一个数据不包含\0rit--;while (rit != s.rbegin())//不能用小于大于或者小等大等{cout << *rit;//可以像指针那样访问rit--;//和正向迭代器不一样,反向迭代器++往左走}cout << *rit;return 0;
}

注意:一个对象被const修饰那么这个对象迭代器的类型就要改变

代码示例:

	const string s("abcd");string::const_iterator it = s.end();

那么反向迭代器也是一样,只要对象被const修饰。

3.5、string 的 capacity 介绍

 根据二八原则我们只要学习一下常用的就行。

1)size 和 length是一样的,但是由于 size 在其他容器也有所以这里推荐使用size。

用法:计算字符串的数据个数(不包含\0)

代码示例:

	const string s("abcd");cout << s.size();//打印结果是4

2)max_size介绍

        这个准确来说没什么意义,他是计算默认的最大的长度,不管这个字符串是长的还是短的。

代码示例:

	const string s("abcd");string s2("hello world");cout << s.max_size() << endl;cout << s2.max_size();

打印结果:

3)capacity介绍

        capacity是计算能存储的数据大小,即:容量;但是实际上容量会多一个来存储\0。

代码示例:

	const string s("abcd");string s2("hello world");cout << s.capacity() << endl;cout << s2.capacity();

 打印结果:

capacity是个默认值:15

4) clear 介绍

        clear就是把size置为0,容量不变。

代码示例:

	string s("abcd");cout << s.capacity() << endl;cout << s.size() << endl;s.clear();//把size变成0.容量不变cout << s.capacity() << endl;cout << s.size();

打印结果:

注意:字符串也没有了。

5)reserve 介绍

        在学习 reserve 之前我们先了解一下string的扩容机制。

代码示例:

	string s;int cap = s.capacity();cout << cap << endl;for (int i = 0; i < 200; i++){s.push_back('x');//尾插数据if (cap != s.capacity())//判断扩容机制{cout << s.capacity() << endl;cap = s.capacity();}}

打印结果:

结论:VS除了第一次是2倍扩容,后面都是1.5倍扩容。不同普通平台的扩容倍数是不一样的;Linux是2倍扩容。

string的扩容机制是异地扩容,比如:size 为4,容量为15,它先在别的地方申请容量为31的空间,再把那4个数据复制过去然后再把原来的空间释放掉。

reserve 如果扩容的大小比capacity小,会不会缩容是不确定的(不建议用reserve进行缩容),看平台,对size不影响。

代码示例:

	string s;cout << s.capacity() << endl;s.reserve(100);cout << s.capacity() << endl;s.reserve(200);cout << s.capacity() << endl;s.reserve(100);cout << s.capacity() << endl;

打印结果:

 注意: 在VS下 reserve 扩容会比指定的值大,缩容有可能缩也可能不缩。

 6)resize 介绍

        resize有三种情况:size<n<capacity(插入数据)、n>capacity(扩容+插入数据)、n<size(删除数据)。

图片解疑:

注意:插入数据默认为\0

代码示例:

int main()
{string s("hello world");s.resize(20);//扩容后面默认是\0cout << s << endl;s.resize(5);//删除数据cout << s << endl;s.resize(20, 'Y');//扩容指定插入数据Ycout << s << endl;return 0;
}

 运行结果:

 3.5、string的修改和访问介绍

 1)[ ] 和 at 介绍

        [ ] 使得string像数组那样进行修改和访问。

代码示例:

	string s("hello world");for (int i = 0; i < s.size(); i++){cout << s[i] << ' ';}cout << endl;for (int j = 0; j < s.size(); j++){s[j]++;}cout << s << endl;

运行结果:

         at也可以对string进行访问和修改。

代码示例:

	string s("hello world");for (int i = 0; i < s.size(); i++){cout << s.at(i) << ' ';}cout << endl;for (int j = 0; j < s.size(); j++){s.at(j)++;}cout << s << endl;

运行结果和上面的一样。

        他们两个的区别在于越界检查,[ ] 对于越界处理是断言报错,at是抛异常。

代码示例:

	string s("hello world");s[15];

运行结果:

string s("hello world");s.at(13);

 运行结果:

	string s("hello world");s.push_back('abc');cout << s;

注意:断言报错直接把程序终止了,很恶心,但是抛异常我们只要把异常捕获了久不会终止程序。

2)push_back介绍

        pushi_back就是尾插数据。

代码示例:

	string s("hello world");s.push_back('a');//只能尾插单个字符cout << s;

运行结果:

3)append介绍

        尾插字符串火车字符串对象。

代码示例:

	string s("hello world");s.append("abcd");cout << s << endl;string s2("cde");s.append(s2);cout << s << endl;

运行结果:

4)+=介绍

        它可以尾插字符串或者单个字符或者string对象。

代码示例:

	string s("hello world");s += "abcd";cout << s << endl;string s1("acd");s += s1;cout << s << endl;s += 'g';cout << s;

 运行结果:

注意:关于头插和头删string是不支持的,效率极低。但是有尾删 pop_back。

5)erase 介绍

        删除 string 指定位置的字符。

代码示例:

	string s("hello world");s.erase(0, 1);//删除下标为0的那一个字符cout << s;

 运行结果:

注意:这样的效率是极低的,因为删除了前面的数据,后面的数据要往前面移动。

6)replace介绍

         把字符串中的某一段替换成另一段字符串或者单个字符,如果把字符串中的单个字符替换成多个字符数据就要往后移动,效率极低。

代码示例:

	string s("hello world");s.replace(2, 1, "hhh");//从下标为2的字符串那里开始计算到长度为1的区间代替为“hhh”cout << s;

 运算结果:

 7)c_str 介绍

        返回一个字符串的指针,这个字符串包含\0。

代码示例:

	string s("string6_19.cpp");//当前文件名FILE* fout = fopen(s.c_str(), "r");//打开当前文件,s.c_str()相当于string6_19.cpp,它返回的是这个字符串的指针char ch = fgetc(fout);//获得当前的文件的一个字符while (ch != EOF){cout << ch;ch = fgetc(fout);}

运行结果:

3.6、find 和 substr 介绍

 

        find是寻找某个字符并且返回这个字符的下标;substr是取出从某个下标开始长度为len的字符串。

 代码示例:

	string s("hello world");string ret;size_t pos = s.find('w');//默认从下标为0开始找,没有找到返回npos,就是无符号整形的最大值42亿9千万if (pos != string::npos)//就是找到了{ret = s.substr(pos);//取出w后面(包含w)的字符串}cout << ret;

运行结果:

注意:如果有多个w,想取出最后一个w就要倒着找,使用rfind来找。

代码示例:

	string s("hellow world");string ret;size_t pos = s.rfind('w');//默认从下标为0开始找,没有找到返回npos,就是无符号整形的最大值42亿9千万if (pos != string::npos)//就是找到了{ret = s.substr(pos);//取出w后面(包含w)的字符串}cout << ret;

运行结果和上面一样。

3.7、find_first_of 介绍

        find_first_of 是根据条件找到指定的字符。

代码示例:

	string s("hhh eee ooo ccc");size_t pos = s.find_first_of("hec");//根据条件(hec)从字符串找到包含这些条件的字符,返回它的下标while (pos != string::npos){cout << s[pos] << endl;s[pos] = '*';//修改pos = s.find_first_of("hec",pos + 1);//从下个开始找}cout << s;

运行结果:

七、string类的底层模拟实现

代码示例 :

//stringVR.h
#pragma once
#include<iostream>
#include<stdlib.h>
#include<string>
#include<assert.h>
using namespace std;
namespace LA
{//const size_t string::npos = -1;class string{public://string()//	:_str(new char[1]{'\0'})//	, _size(0)//	,_capacity(0)//{//}//传统写法//string(string& s)//{//	_str = new char[s._capacity + 1];//	_size = s._size;//	_capacity = s._capacity;//	memcpy(_str, s._str,s._size+1);//}//现代写法string(const string& s)//浅拷贝问题:析构多次( 解决办法:引用计数(一个空间被多个对象指向记录指向个数) ),一个修改影响另一个(解决办法:写时拷贝就是深拷贝){//引用计数的写时拷贝优势:赌博心态,赌它不会被修改,就不用开空间。最新版本:进行深拷贝//string tmp(s._str);//只要字符串中间没有\0,不然就会出错string tmp(s.begin(), s.end());swap(tmp);}//进一步写法template<class InputIterator>string(InputIterator first, InputIterator last){while (first != last){pushback(*first);first++;}}typedef char* iterator;//迭代器的模拟实现typedef const char* const_iterator;iterator begin()//指向第一个字符{return _str;}iterator end(){return _str + _size;//指向\0}const_iterator begin()const//指向第一个字符{return _str;}const_iterator end()const{return _str + _size;//指向\0}void reserve(size_t n);void pushback(char ch);void append(const char* str);string& operator+=(char ch);string& operator+=(const char* str);void insert(size_t pos, char ch);void insert(size_t pos,const char* str);void erase(size_t pos, size_t len = npos);size_t find(char ch, size_t pos = 0);size_t find(const char* str, size_t pos = 0);string substr(size_t pos = 0, size_t len = npos);string& operator=(const string& s);string(const char* str=""): _size(strlen(str)){_str = new char[_size + 1]; // +1是给\0也留了个位置_capacity=_size;memcpy(_str, str,_size+1);}const char* c_str()const{return _str;}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}size_t size()const;char& operator[](size_t i);const char& operator[](size_t i)const;bool operator<(const string& s) const;bool operator<=(const string& s) const;bool operator>=(const string& s) const;bool operator==(const string& s) const;bool operator!=(const string& s) const;bool operator>(const string& s) const;void clear();void swap(string& s);private: char* _str;size_t _size;//不包含\0size_t _capacity;//不包含\0public:static const size_t npos = -1;//特殊处理,只有static const int 才行};std::ostream& operator<<(std::ostream& os, const string& s);std::istream& operator>>(std::istream& is, string& s);}
//stringVR.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"stringVR.h"
namespace LA
{size_t string::size() const{return _size;}char& string::operator[](size_t i){assert(i < _size);return _str[i];}const char& string::operator[](size_t i)const{assert(i < _size);return _str[i];}void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];if (_str){memcpy(tmp, _str, _size + 1);delete[] _str;}_str = tmp;_capacity = n;}}void string::pushback(char ch){if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = ch;_str[_size] = '\0';}void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){size_t newcapacity = _size + len > 2 * _capacity ? _size + len : 2 * _capacity;reserve(newcapacity);}memcpy(_str + _size, str, len + 1);_size += len;}string& string::operator+=(char ch){pushback(ch);return *this;}string& string::operator+=(const char* str){append(str);return *this;}void string::insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}size_t end = _size;while (end >= (int)pos){_str[end + 1] = _str[end];end--;}_str[pos] = ch;_size++;}void string::insert(size_t pos, const char* str){assert(pos<=_size);size_t len = strlen(str);if (_size + len > _capacity){size_t newcapacity = _size + len > 2 * _capacity ? _size + len : 2 * _capacity;reserve(newcapacity);}size_t end = _size + len;while (end > pos + len + 1){_str[end] = _str[end - len];end--;}for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;}void string::erase(size_t pos, size_t len){assert(pos < _size);if (len == npos || len >= _size - pos){//删完_str[pos] = '\0';_size = pos;}else{memmove(_str + pos, _str + pos + len, _size + 1 - (pos + len));_size -= len;}}size_t string::find(char ch, size_t pos){for (size_t i = pos; i < _size; i++){if (ch == _str[i]){return i;}}return npos;}size_t string::find(const char* str, size_t pos){const char* p = strstr(_str, str);if (p == nullptr){return npos;}else{return p - _str;}}string string::substr(size_t pos, size_t len){assert(pos < _size);if (len > _size - pos){len = _size - pos;}string ret;for (size_t i = 0; i < len; i++){ret += _str[pos + i];}return ret;}string& string::operator=(const string& s){if (this != &s){string tmp(s);swap(tmp);}return *this;}bool string::operator<(const string& s) const{size_t len1 = _size;size_t len2 = s._size;size_t i1 = 0, i2 = 0;while (i1 < len1 && i2 < len2){if (_str[i1] < s._str[i2]){return true;}else if (_str[i1] > s._str[i2]){return false;}else{i1++;i2++;}}return i1 == len1 && i2 < len2;}bool string::operator<=(const string& s) const{return *this < s || *this == s;}bool string::operator>=(const string& s) const{return !(*this < s);}bool string::operator==(const string& s) const{size_t len1 = _size;size_t len2 = s._size;size_t i1 = 0, i2 = 0;while (i1 < len1 && i2 < len2){if (_str[i1] != s._str[i2]){return false;}else{i1++;i2++;}}return  i1 == len1 && i2 == len2;}bool string::operator!=(const string& s) const{return !(*this == s);}bool string::operator>(const string& s) const{return !(*this <= s);}std::ostream& operator<<(std::ostream& os, const string& s){for (size_t i = 0; i < s.size(); i++){os << s[i];}return os;}void string::clear(){_str[0] = '\0';_size = 0;}void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}std::istream& operator>>(std::istream& is, string& s){s.clear();char buff[256];size_t i = 0;char ch=is.get();//is >> ch;while (ch != ' '&&ch != '\n'){buff[i++] = ch;ch=is.get();if (i == 255){buff[i] = '\0';s += buff;i = 0;}}if (i > 0){buff[i] = '\0';s += buff;}return is;}}

完!!

注意:本文只介绍一些string常见接口,具体接口请到官网进行查看!

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

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

相关文章

深入剖析Linux epoll模型:从LT/ET模式到EPOLLONESHOT的实战指南

一、epoll&#xff1a;高性能I/O复用的核心引擎 epoll是Linux内核2.6引入的高效I/O多路复用机制&#xff0c;专为解决C10K问题而生。相比select/poll&#xff0c;epoll在连接数激增时性能优势显著&#xff1a; // 创建epoll实例 int epollfd epoll_create1(0);// 事件注册 s…

网络安全之某cms的漏洞分析

漏洞描述 该漏洞源于Appcenter.php存在限制&#xff0c;但攻击者仍然可以通过绕过这些限制并以某种方式编写代码&#xff0c;使得经过身份验证的攻击者可以利用该漏洞执行任意命令 漏洞分析 绕过编辑模板限制&#xff0c;从而实现RCE 这里可以修改模板文件&#xff0c;但是不…

Nginx-前端跨域解决方案!

1 Nginx 核心 Nginx 是一个开源的高性能 HTTP 和反向代理服务器&#xff0c;以轻量级、高并发处理能力和低资源消耗著称。除作为 Web 服务器外&#xff0c;还可充当邮件代理服务器和通用的 TCP/UDP 代理服务器&#xff0c;广泛应用于现代 Web 架构中。 在 Windows 系统中使用…

RedisVL 入门构建高效的 AI 向量搜索应用

一、前置条件 在开始之前&#xff0c;请确保&#xff1a; 已在 Python 环境中安装 redisvl。运行 Redis Stack 或 Redis Cloud 实例。 二、定义索引架构&#xff08;IndexSchema&#xff09; 索引架构&#xff08;IndexSchema&#xff09;用于定义 Redis 的索引配置和字段信…

基于ssm移动学习平台微信小程序源码数据库文档

摘 要 由于APP软件在开发以及运营上面所需成本较高&#xff0c;而用户手机需要安装各种APP软件&#xff0c;因此占用用户过多的手机存储空间&#xff0c;导致用户手机运行缓慢&#xff0c;体验度比较差&#xff0c;进而导致用户会卸载非必要的APP&#xff0c;倒逼管理者必须改…

【Python】Tkinter模块(巨详细)

专栏文章索引:Python 有问题可私聊:QQ:3375119339 本文内容系本人根据阅读的《Python GUI设计tkinter从入门到实践》所得,以自己的方式进行总结和表达。未经授权,禁止在任何平台上以任何形式复制或发布原始书籍的内容。如有侵权,请联系我删除。 目录 一、Tkinter与GUI …

【C++特殊工具与技术】局部类

在 C 的类体系中&#xff0c;除了全局类、嵌套类&#xff08;在类内部定义的类&#xff09;&#xff0c;还有一种特殊的存在 ——局部类&#xff08;Local Class&#xff09;。它像函数内部的 “封闭王国”&#xff0c;作用域严格限制在所属函数内&#xff0c;既拥有类的封装特…

《C#图解教程 第5版》深度推荐

《C#图解教程 第5版》深度推荐 在 C# 编程语言的浩瀚学习资源中&#xff0c;《C#图解教程 第5版》宛如一座灯塔&#xff0c;为开发者照亮前行之路。通过其详实的目录&#xff0c;我们能清晰窥见这本书在知识架构、学习引导上的匠心独运&#xff0c;无论是编程新手还是进阶开发者…

【Kubernetes】配置自定义的 kube-scheduler 调度规则

在最近一次 K8s 环境的维护中&#xff0c;发现多个 Pod 使用相同镜像时&#xff0c;调度到固定节点的问题导致集群节点资源分配不均的情况。 启用调度器的打分日志后发现这一现象是由 ImageLocality 打分策略所引起的&#xff08;所有的节点中&#xff0c;只有一个节点有运行该…

跟着AI学习C# Day21

&#x1f4c5; Day 21&#xff1a;动态类型与动态语言运行时&#xff08;Dynamic Types & DLR&#xff09; ✅ 学习目标&#xff1a; 理解什么是 dynamic 类型&#xff1b;掌握 dynamic 与 object 的区别&#xff1b;理解 DLR&#xff08;Dynamic Language Runtime&#…

leetcode-3085.成为K字符串需要删除的最小字符串数

题目描述 解题思路 这题不难想到需要统计每个字母的出现频率&#xff0c;一共有26个字母&#xff0c;故cnt数组有26维。我们可以枚举其中一种作为「删除操作结束后出现频率最低的字符」&#xff0c;将其设置为 c&#xff0c;那么所有频率小于 c 的字符都会被删除&#xff0c;所…

Android 中 解析 XML 文件的几种方式

在 Android 开发中,解析 XML 文件有多种方式,每种方式都有其特点和适用场景。常见的 XML 解析方式有 DOM 解析、SAX 解析 和 XmlPullParser 解析。 一、xml 文件及数据类 1、xml 文件 将测试用 book.xml 文件放在项目的 app/src/main/assets 目录下,文件内容如下:<lib…

python里的abc库是什么东西

Python 中的 ABC&#xff1a;为什么你需要抽象基类&#xff1f;告别“假鸭子”&#xff0c;拥抱真抽象&#xff01; 你是不是经常在 Python 项目中感到困惑&#xff1a;我定义了一个类&#xff0c;希望它能被其他类继承并实现某些特定功能&#xff0c;但又不想它被直接实例化&…

设计模式精讲 Day 9:装饰器模式(Decorator Pattern)

【设计模式精讲 Day 9】装饰器模式&#xff08;Decorator Pattern&#xff09; 文章内容 在软件开发中&#xff0c;灵活扩展功能是提升系统可维护性和可复用性的关键。装饰器模式作为一种结构型设计模式&#xff0c;为对象动态地添加职责&#xff0c;而无需通过继承来实现。它…

浏览器无法访问:Nginx下的基于域名的虚拟主机

检查步骤如下&#xff1a; 1、nginx -t &#xff0c;检查配置文件是否有语法错误 [root89 ~]# nginx -t nginx: the configuration file /opt/nginx/conf/nginx.conf syntax is ok nginx: configuration file /opt/nginx/conf/nginx.conf test is successful # 可以看到 配置…

【appium】6.appium遇到的问题

1.appium-python-client 修改版本1.5 为5.1.1,后执行python程序时&#xff0c;提示&#xff1a; raise TypeError( TypeError: missing 1 required keyword-only argument: options (instance of driver options.Options class) 你遇到的错误&#xff1a; TypeError: missing…

C++法则3:使用拷贝和交换的赋值运算符自动就是异常安全的,且能正确处理自赋值。

C法则3&#xff1a;使用拷贝和交换的赋值运算符自动就是异常安全的&#xff0c;且能正确处理自赋值。 这条法则强调了使用"拷贝和交换"(Copy-and-Swap)惯用法来实现赋值运算符()的优点&#xff1a; 关键点 异常安全&#xff1a;拷贝和交换方法天然提供了强异常安全…

纯血HarmonyOS5 打造小游戏实践:扫雷(附源文件)

鸿蒙扫雷游戏的核心架构设计 鸿蒙OS扫雷游戏采用了MVC&#xff08;模型-视图-控制器&#xff09;的架构思想&#xff0c;将游戏逻辑与UI展示分离&#xff0c;使得代码结构清晰且易于维护。整个游戏由以下几个核心部分构成&#xff1a; 数据模型设计 游戏的基础数据模型是Cel…

Linux C语言的opendir如何获取目录下的隐藏文件

在 Linux 文件系统中&#xff0c;所谓隐藏文件是文件名以 . 开头的文件&#xff08;例如 .bashrc、.git、.config 等&#xff09;。 在编程层面&#xff0c;opendir readdir 并不会自动排除隐藏文件。 只要你不在代码中手动过滤&#xff0c;readdir 会把目录下所有文件&#…

母线槽接头过热隐患难防?在线测温方案实时守护电力安全

近年来&#xff0c;由于各种设备对电力的大力需求&#xff0c;并有逐年增加的趋势&#xff0c;传统电路接线方式在施工时越来越力不从心。系统一旦定型&#xff0c;后续想要简化变更更是难上加难。母线槽方案因此兴起&#xff0c;凭借多点连接&#xff08;接头、插接头、插接箱…