ʕ • ᴥ • ʔ

づ♡ど

 🎉 欢迎点赞支持🎉

个人主页:励志不掉头发的内向程序员;

专栏主页:C++语言;


文章目录

前言

一、STL简介

二、string类的优点

三、标准库中的string类

四、string的成员函数

4.1、构造函数

 4.2、析构函数

4.3、赋值重载

4.4、运算符重载

4.5、迭代器(iterator)

4.6、Capacity相关函数

4.6.1 size/length函数

4.6.2、max_size函数

4.6.3、capacity函数

4.6.4、reserve/resize函数

4.6.5、clear函数

4.6.6、empty函数

4.6.7、shrink_to_fit函数

4.7、插入删除相关函数

4.7.1、push_back函数

4.7.2、append函数

4.7.3、operator+=函数

4.7.4、insert函数

4.7.5、pop_back函数

4.7.6、erase函数

4.7.7、replace函数

4.8、查找函数

4.8.1、find函数

4.8.2、rfind函数

4.8.3、find_first_of函数

4.8.4、find_last_of函数

4.8.5、find_first_not_of/find_last_not_of函数

4.9、其他函数

4.9.1、c_str函数

4.9.2、copy函数

4.9.3、substr函数

五、非成员函数

5.1、operator+函数

5.2、<>函数

5.3、getline函数

总结


前言

本章节我们就来说说我们C++与C语言又一个巨大的区别,那就是STL库,在这个库中收录了我们很多经常使用的数据结构和算法,我们之后的章节会来具体的讲解和学习,接下来我们来一起看看吧。


一、STL简介

STL是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构和算法的软件框架。

STL的六大组件

二、string类的优点

与C语言相比,我们STL容器中(严格来说应该是C++标准库)引入了我们的string,它的出现帮助我们去管理字符串,这使得我们对字符串的使用更加规范以及更加简单快捷,出错率也与自己去实现相比大大的降低了。

三、标准库中的string类

我们可以看看我们string类中有哪些成员函数,string - C++ Reference

我们可以粗略的看到我们string类的成员变量是很多的,我们为了完全搞懂string类,我们就来一个一个成员函数的学习和模拟实现吧。

四、string的成员函数

我们string类都保存在我们string的头文件中

#include <string>

4.1、构造函数

我们学习类第一步就应该先去查看它的构造函数

我们string设计时间比STL早,有很多设计没有参考,所以我们学习时就会觉得它有些设计是十分冗余的,就比如光是string的构造函数就有7种,但是没有关系,只有string是这样的,我们只要把经常要用的学会就行,我也只会模拟实现经常要用的成员函数。

#include <string>int main()
{string s1;string s2("11111111");string s3(s2);// 重载了流输入输出cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cin >> s1;cout << s1 << endl;return 0;
}

上面3个是我们主要使用的构造函数,接下来讲讲剩下的几个。

第3个构造函数是用来构造一个字符串的一部分的。

第一个参数是用那个字符串构造,第二个则是从哪个位置,第三个参数是拷贝多少字符(如果剩下的不够就有多少拷贝多少)。

#include <string>int main()
{string s1("hello world");string s2(s1, 6, 5);string s3(s1, 6, 100);cout << s2 << endl;cout << s3 << endl;return 0;
}

当然如果我们不传最后一个参数,那就是默认拷贝到结尾,它的缺省参数是一个npos

它是一个静态成员变量。我们可以试着突破类域去看看它的值是多少。

#include <string>int main()
{cout << string::npos << endl;return 0;
}

由于我们的字符串不可能有这么长,所以这就可以默认拷贝到最后一个位置了。

第5个构造是用来构造字符串前面一部分的。

第一个参数是用来输入字符串的,第二个参数则是确定拷贝前面几个字符,要是字符数目不够则是全拷贝。

#include <string>int main()
{string s1("hello world", 5);string s2("hello world", 100);cout << s1 << endl;cout << s2 << endl;return 0;
}

第6个构造就是用n个字符c去初始化。

第一个参数就是要几个字符,第二个参数就是字符是什么。

#include <string>int main()
{string s1(10, 'x');cout << s1 << endl;return 0;
}

还有最后一个暂时不讲,之后再说明。

 4.2、析构函数

析构函数没有什么必要去看,因为它是系统自动调用的,我们无需手动调用。

4.3、赋值重载

第一种就是把一个string赋值给另外一个string。

int main()
{string s1("xxxxxxxxxxxxx");string s2;s2 = s1;cout << s2 << endl;return 0;
}

第二种是把我们的字符串赋值给我们的string。

int main()
{string s1;s1 = "xxxxxxxxxx";cout << s1 << endl;return 0;
}

第三种则是把我们字符赋值给我们string。

int main()
{string s1;s1 = 'X';cout << s1 << endl;return 0;
}

我们一般使用第一种和第二种即可,第三种赋值重载没有必要,因为一个字符也算字符串,也可以用第二种代替。

4.4、运算符重载

[ ]符重载:

我们string类将[]符进行了重载,这无疑是一个高明的操作,因为这样就使得我们的字符串和数组一样可以非常方便的修改和查询了。

int main()
{string s1("hello world");//修改指定字符s1[3] = '*';s1[5] = '#';cout << s1 << endl;//查看指定字符cout << s1[4] << " " << s1[7] << endl;return 0;
}

同时我们还可以通过[]符来遍历我们的字符串。

int main()
{string s1("hello world");//size()是string中返回我们字符串大小的函数for (int i = 0; i < s1.size(); i++){cout << s1[i] << " ";}cout << endl;return 0;
}

这样操作主要是通过我们的运算符通过返回我们类型的引用去修改指定位置的字符来实现的,我们下一章节再来细聊。
除了这种遍历变量的方式,我们还有两种,下面会讲。

4.5、迭代器(iterator)

我们的迭代器是STL的一大组件,其作用主要就是用来遍历和访问我们的容器。我们先来看看它的操作。

int main()
{string s1("hello world");//size()是string中返回我们字符串大小的函数string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;return 0;
}

这样写就实现了我们遍历的效果。我们仔细观察这些代码,感觉就很像指针。我们迭代器的实现千变万化,只要能够达到我们所需要的效果即可,所以我们也不确定它到底是怎么实现的,可能有的就是用指针实现的。但是我们可以想象成指针。

我们的begin()是返回string空间开始位置的迭代器,而end()是我们返回最后一个有效字符的位置,也就是'\0'的位置的迭代器。

大致就是这个样子,我们从begin到end,一个一个遍历过去,如果是指针,那就是一个一个解引用后再++,一直到end后停止,这个很好理解,如果不是指针,那我们就得运算符重载了,把我们'*'、'++'两个运算符重载成我们和指针那样的操作了。所以我们的迭代器可能是指针,也可能不是。

iterator给我们STL库中所有的容器提供了一个通用的遍历和访问的方法,让它们统一起来,这就是迭代器的厉害之处。

当然,我们除了这种正向迭代器,还有反向迭代器

int main()
{string s1("hello world");string::reverse_iterator rit = s1.rbegin();while (rit != s1.rend()){cout << *rit << " ";rit++;}cout << endl;return 0;
}

它的写法和刚才的正向迭代器很像。

它也是使用++,只不过是倒着走的。我们可以这样子理解

我们封装了++后使其倒着运行。

当然,有的时候我们会碰到const string,但是我们的普通迭代器可读可写,会导致权限放大不能调用怎么办呢?这个时候就得用const_iterator迭代器了。用法和iterator是一样的,只是不能修改,只能遍历。

int main()
{string s1("hello world");string::const_iterator cit = s1.cbegin();while (cit != s1.cend()){cout << *cit << " ";cit++;}cout << endl;return 0;
}

当然,也有const反向迭代器

int main()
{string s1("hello world");string::const_reverse_iterator crit = s1.crbegin();while (crit != s1.crend()){cout << *crit << " ";crit++;}cout << endl;return 0;
}

用法都是类似的,大家记住就好了,等下一章节讲解模拟实现是在来细讲这些内容。

我们还有一种遍历变量的方法,就是范围for

int main()
{string s1("hello world");for (auto ch : s1){cout << ch << " ";}cout << endl;return 0;
}

这个代码的含义是我们从我们的s1中去取我们的每一个变量,然后传给我们的ch(自定义的,叫啥无所谓)中去。

我们再来聊聊auto,它代表的是自动推导类型,它会自动去推导我们s1传过来的是什么类型的变量,auto就是什么类型的,这里是char,所以auto就是char,当然我们自己写char也可以。

我们auto的主要作用是来简化我们的代码而使用的,例如map<string, string>::iterator可以直接用auto代替,是不是简单多了。但是它的代价是牺牲了可读性。

当然我们auto必须要初始化,不然它就不知道它因该推导成什么类型。也不能一行定义不同类型的变量。也不能定义数组。

// 不能这样写
auto a;
auto a = 1, b = 1.1;
auto array[] = { 1, 2, 3, 4, 5, 6 };

我们的auto不能做参数,但是可以做返回值,虽然不建议这么做。

我们接着说范围for,

	for (char ch : s1){cout << ch << " ";}

但是我们这里一般都用auto。范围for可以自动赋值,自动迭代,自动判断结束,它的底层其实是迭代器,虽然看上去很厉害,但其实就是迭代器套了一层壳。

同时他不会去改变我们s1的内容,它相当于是传*it给我们的ch而非it。

int main()
{string s1("hello world");for (auto ch : s1){ch -= 2;cout << ch << " ";}cout << endl;cout << s1;return 0;
}

我们可以看出它是没有修改s1的。

如果是想要修改得在auto后加&,相当于从之前传输过来的char类型变成了char&类型

int main()
{string s1("hello world");for (auto& ch : s1){ch -= 2;cout << ch << " ";}cout << endl;cout << s1;return 0;
}

我们所讲的3种遍历方式在性能方面是没有任何区别的,可以随便使用。

4.6、Capacity相关函数

4.6.1 size/length函数

这两个函数都是用来返回我们的字符串中字符的数量的,使用方法也很简单

int main()
{string s1("hello world");cout << s1.size() << endl;cout << s1.length() << endl;return 0;
}

它们就是两个不同名字但是作用完全一样的函数。我们length不具有通用性,只属于string容器,但是我们的size是所有容器都有的。

4.6.2、max_size函数

这个函数的作用就是和你说说我们的string最大能开多大,实际就是整型的最大值。没有什么用处,而且过于理想化,实际上根本开不了这么大。

int main()
{string s1("hello world");cout << s1.max_size() << endl;return 0;
}

4.6.3、capacity函数

返回我们string容量的函数。

int main()
{string s1("hello world");cout << s1.capacity() << endl;return 0;
}

我们可以先来看看我们的string是怎么扩容的

int main()
{string s;// 得到初始内存size_t sz = s.capacity();cout << "capacity changed: " << sz << endl;cout << "make s grow:" << endl;for (int i = 0; i < 100; i++){// 循环插入c去看它扩容s.push_back('c');// 如果扩容了就打印看看新扩容的大小if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << endl;}}
}

我们可以看到,我们第一次是2倍扩容,其他的时候就是1.5倍扩容。这是因为我们vs的工程师为了防止我们总是使用不满string,为了效率所以在string上增加了一个buffer[16]的字符串,当我们的字符串没有满16个字符时我们的编译器就不会在堆上开辟空间,而是在栈上的buffer的字符串中。

所以我们从第一次扩容是2倍扩容是特殊情况,后面都是1.5倍扩容。当然不同编译器扩容方式不一样。

4.6.4、reserve/resize函数

reserve是用来开辟空间的,它能提前开辟好我们指定用到的空间,从而减少扩容次数。

int main()
{string s;s.reserve(100);cout << s.capacity() << endl;return 0;
}

但是其实这个只是让我们编译器至少开辟100的空间,具体是多多少得看编译器操作,因为编译器会做整数倍对齐。

当然,我们的reserve是一般是不会缩容的,但是还是要看具体的服务器。

int main()
{string s("0123456789");cout << s.capacity() << endl;s.reserve(5);cout << s.capacity() << endl;s.reserve(20);s.reserve(15);cout << s.capacity() << endl;return 0;
}

resize则是可以扩容插入,如果它比size小会删除数据,如果比size大但是capacity小的话会在后面插入数据,如果你没给会插入默认字符' \0 ',如果比capacity大则会扩容。

4.6.5、clear函数

这个函数是用来清空我们的字符串的数据,但是一般不会清理容量

int main()
{string s("0123456789");cout << s << endl;s.clear();cout << s << endl;cout << s.capacity() << endl;return 0;
}

4.6.6、empty函数

判断我们string是不是空的,有没有字符。

int main()
{string s("0123456789");if (s.empty()) cout << "Yes" << endl;else cout << "No" << endl;s.clear();if (s.empty()) cout << "Yes" << endl;else cout << "No" << endl;return 0;
}

4.6.7、shrink_to_fit函数

这个函数也是用来缩容的,但是和reserve一样也不一定会成功。

4.7、插入删除相关函数

4.7.1、push_back函数

这个函数的功能是尾插一个字符,它只能插入一个字符,不能插入字符串,设计的蛮鸡肋的。

int main()
{string s("1234");// 如果用双引号就会报错,因为不能插入字符串s.push_back('5');cout << s << endl;return 0;
}

4.7.2、append函数

想要插入字符串就可以用append,但是一般我们就使用第三个,别的没有必要使用,这里简单介绍一下把

第一个:

单纯的在末尾插入一个string字符串。

int main()
{string s1("hello ");string s2("world");s1.append(s2);cout << s1 << endl;return 0;
}

第二个:

在末尾插入字符串str从subpos位置开始的sublen个字符。

int main()
{string s1("hello ");string s2("world");s1.append(s2, 1, 3);cout << s1 << endl;return 0;
}

如果剩余数不足那就取到末尾。

第三个:

直接在后面插入一个字符串。

int main()
{string s1("hello ");s1.append("world");cout << s1 << endl;return 0;
}

第四个:

在末尾插入s前n个字符,如果剩下的不够,则取到末尾。

int main()
{string s1("hello ");s1.append("world", 3);cout << s1 << endl;return 0;
}

第五个:

就是在末尾插入n给字符c。

第六个后面再将。

4.7.3、operator+=函数

这个是我们string中最常用的尾插用法,它可以兼容字符和字符串,而且可读性也很好。

int main()
{string s1("hello");string s2("world");s1 += ' ';s1 += s2;s1 += "################";cout << s1 << endl;return 0;
}

4.7.4、insert函数

我们前面就学了尾插,其他位置的插入就用这个函数,这个函数也十分的冗余,主要用的就2个,如果我们要插入我们的字符串,可以用第1种和第3种。

int main()
{string s1("hello");string s2("world");// 第一种方法s1.insert(4, s2);cout << s1 << endl;// 第二种方法s1.insert(0, "########");cout << s1 << endl;return 0;
}

插入字符也可以把字符当字符串,但是如果一定得是字符的话那我们就只能用第五种或者第六种办法了。

int main()
{string s1("hello");char c = '#';// 第五种方法,在第0位置插入1个字符cs1.insert(0, 1, c);cout << s1 << endl;// 第六种方法s1.insert(s1.end(), c);cout << s1 << endl;return 0;
}

其他的不常用,大家可以自己去试试看看怎么用,很简单。

4.7.5、pop_back函数

这个就不用多说了,就是尾删。

4.7.6、erase函数

主要就是使用第1个,在pos位置删除len个字符,如果没写len或者剩下的不足那就在pos后面全删了。

int main()
{string s1("hello world");s1.erase(2, 2);cout << s1 << endl;return 0;
}

第二个则是迭代器删除,只能删除一个,

int main()
{string s1("hello world");s1.erase(s1.begin() + 2);cout << s1 << endl;return 0;
}

第3个则是删除一个区间的。

4.7.7、replace函数

这个函数的主要作用就是替换,设计的也很麻烦,就讲几个经常使用的,大家要是有需要的再查库就好了。

int main()
{string s1("hello world");// 第5个位置替换成"%%",替换3个字符s1.replace(5, 3, "%%");cout << s1 << endl;return 0;
}

4.8、查找函数

4.8.1、find函数

我们可以查找一个string字符串,或者一个字符,它如果找到了就会返回第一个匹配位置的字符下标,没找到就会返回npos。这里面的pos表示从哪里开始找起。

int main()
{string s1("hello world");string s2("wor");size_t pos = s1.find(s2);cout << pos << endl;// 从第7个位置开始找pos = s1.find("s2, 7");cout << pos << endl;pos = s1.find(' ');cout << pos << endl;return 0;
}

4.8.2、rfind函数

rfind和find用法相同,就是rfind是倒着查找的,这里就不多赘述了。返回值就是从后往前第一次出现的下标,如果没有就返回npos。

4.8.3、find_first_of函数

这个函数的意思不是说找第一个,因为我们find就是第一个,而是说找任意一个,就比如

int main()
{std::string str("Please, replace the vowels in this sentence by asterisks.");std::size_t found = str.find_first_of("abcd");// 一直查找,知道找不到为止while (found != std::string::npos){str[found] = '*';// 从已经找过的位置接着往下找found = str.find_first_of("abcd", found + 1);}std::cout << str << '\n';return 0;
}

我们这个函数就是把我们传进去的字符串"abcd"中的每一个字符和我们的原文进行配对,如果有其中有其中一个字符可以配对上那就返回第一个配对上的下标,所以上面我们str字符串中的a、b、c、d字符全部替换成了我们的*号。

4.8.4、find_last_of函数

和我们的find_first_of一样,只不过是反向查找。

4.8.5、find_first_not_of/find_last_not_of函数

分别是find_first_of和find_last_of的对立,就是查找除了我们字符串中的字符其他的字符。

int main()
{std::string str("Please, replace the vowels in this sentence by asterisks.");std::size_t found = str.find_first_not_of("abcd");while (found != std::string::npos){str[found] = '*';found = str.find_first_not_of("abcd", found + 1);}std::cout << str << '\n';return 0;
}

4.9、其他函数

4.9.1、c_str函数

此函数的作用就是返回底层字符串的指针。这个函数的意义就是兼容C语言的。

4.9.2、copy函数

这就是一个拷贝,一般用的很少用,而且我们还要手动的去给它增加' \0 ',不然他就无法检测到什么时候停下,它的返回值就是拷贝了多少个字符。

int main()
{string s1("hello world");char buffer[10];// 从s1中的第3个字符开始拷贝4个字符size_t len = s1.copy(buffer, 4, 3);// 手动增加\0,不然就会出错cout << buffer << endl;buffer[len] = '\0';cout << buffer << endl;return 0;
}

4.9.3、substr函数

这个是我们获取字串最常用的方法。是从pos开始的len个字符,如果不给len就拷贝到结尾。

int main()
{string s1("0123456789");string s2, s3;s2 = s1.substr(4, 7);s3 = s1.substr(5);cout << s2 << endl;cout << s3 << endl;return 0;
}

五、非成员函数

5.1、operator+函数

我们operator+没有重载成成员函数的原因是为了方便更加自由的操作

int main()
{string s1("hello");// 这个重载成成员函数就可以做到string s2 = s1 + "world";cout << s2 << endl;// 这样如果是重载函数那就做不到了string s3 = "world" + s1;cout << s3 << endl;return 0;
}

这样就自由了很多。

5.2、<</>>函数

不多赘述,前往都用了很多次了。

5.3、getline函数

这个函数的作用就是改变我们字符串的分割符,我们的字符串默认是以空格和换行符作为分割,这就使得我们有的时候想要记录一些句子却记录不了,因为有空格。

int main()
{string s1;cin >> s1;cout << s1;return 0;
}

为了避免这种情况,我们就可以用getline来改变分隔符。它的第一个参数是流输入,一般是cin,第二个参数就是我们要输入的字符串,第三个参数就是我们想要的分隔符,如果不写就默认是换行为分隔符。

int main()
{string s1;getline(cin, s1);cout << s1;return 0;
}

以*为分隔符

int main()
{string s1;getline(cin, s1, '*');cout << s1;return 0;
}


总结

以上便是我们string的各种常用函数的使用方法,虽然很多,但是都不是很难,我们也不用死记硬背,我们之后和日常使用会一次又一次的反复记忆,我们如果忘记了再回来或者查文档去看看就好了,下一章节我们回来试着模拟实现string,下一章节再见。

🎇坚持到这里已经很厉害啦,辛苦啦🎇

ʕ • ᴥ • ʔ

づ♡ど

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

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

相关文章

登上Nature!清华大学光学神经网络研究突破

2025深度学习发论文&模型涨点之——光学神经网络光学神经网络的基本原理是利用光的传播、干涉、衍射等特性来实现神经网络中的信息处理和计算。在传统神经网络中&#xff0c;信息以电信号的形式在电子元件之间传输和处理&#xff0c;而在光学神经网络中&#xff0c;信息则以…

【java】对word文件设置只读权限

文件流输出时 template.getXWPFDocument().enforceCommentsProtection(); 文件输出时 //打开己创建的word文档 XWPFDocument document new XWPFDocument(new FileInputStream("output.docx")); //设置文档为只读 document.enforceReadonlyProtection(); //保存文…

Zookeeper 在 Kafka 中扮演了什么角色?

在 Apache Kafka 的早期架构中&#xff0c;ZooKeeper 扮演了分布式协调服务角色&#xff0c;负责管理和协调整个 Kafka 集群。 尽管新版本的 Kafka 正在逐步移除对 ZooKeeper 的依赖&#xff0c;但在许多现有和较早的系统中&#xff0c;了解 ZooKeeper 的作用仍然非常重要。 Zo…

什么叫做 “可迭代的产品矩阵”?如何落地?​

“可迭代的产品矩阵” 不是静态的产品组合&#xff0c;而是围绕用户需求与商业目标构建的动态生态。它以核心产品为根基&#xff0c;通过多维度延伸形成产品网络&#xff0c;同时具备根据市场反馈持续优化的弹性&#xff0c;让产品体系既能覆盖用户全生命周期需求&#xff0c;又…

Nginx代理配置详解:正向代理与反向代理完全指南

系列文章索引&#xff1a; 第一篇&#xff1a;《Nginx入门与安装详解&#xff1a;从零开始搭建高性能Web服务器》第二篇&#xff1a;《Nginx基础配置详解&#xff1a;nginx.conf核心配置与虚拟主机实战》第三篇&#xff1a;《Nginx代理配置详解&#xff1a;正向代理与反向代理…

Vue3 Element-plus 封装Select下拉复选框选择器

废话不多说&#xff0c;样式如下&#xff0c;代码如下&#xff0c;需要自取<template><el-selectv-model"selectValue"class"checkbox-select"multiple:placeholder"placeholder":style"{ width: width }"change"change…

jenkins 自动部署

一、win10 环境安装&#xff1a; 1、jdk 下载安装&#xff1a;Index of openjdk-local 2、配置环境变量&#xff1a; 3、jenkins 下载&#xff1a;Download and deploy 下载后的结果&#xff1a;jenkins.war 4、jenkins 启动&#xff1a; 5、创建管理员用户 admin 登录系统…

2020 GPT3 原文 Language Models are Few-Shot Learners 精选注解

本文为个人阅读GPT3&#xff0c;部分内容注解&#xff0c;由于GPT3原文篇幅较长&#xff0c;且GPT3无有效开源信息 这里就不再一一粘贴&#xff0c;仅对原文部分内容做注解&#xff0c;仅供参考 详情参考原文链接 原文链接&#xff1a;https://arxiv.org/pdf/2005.14165 语言模…

设计模式笔记_行为型_迭代器模式

1. 迭代器模式介绍迭代器模式&#xff08;Iterator Pattern&#xff09;是一种行为设计模式&#xff0c;旨在提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而又不需要暴露该对象的内部表示。这个模式的主要目的是将集合的遍历与集合本身分离&#xff0c;使得用户可…

【Part 4 未来趋势与技术展望】第一节|技术上的抉择:三维实时渲染与VR全景视频的共生

《VR 360全景视频开发》专栏 将带你深入探索从全景视频制作到Unity眼镜端应用开发的全流程技术。专栏内容涵盖安卓原生VR播放器开发、Unity VR视频渲染与手势交互、360全景视频制作与优化&#xff0c;以及高分辨率视频性能优化等实战技巧。 &#x1f4dd; 希望通过这个专栏&am…

mac查看nginx安装位置 mac nginx启动、重启、关闭

安装工具&#xff1a;homebrew步骤&#xff1a;1、打开终端&#xff0c;习惯性命令&#xff1a;brew update //结果&#xff1a;Already up-to-date.2、终端继续执行命令&#xff1a;brew search nginx //查询要安装的软件是否存在3、执行命令&#xff1a;brew info nginx4. …

网络通信的基本概念与设备

目录 一、互联网 二、JAVA跨平台与C/C的原理 1、JAVA跨平台的原理 2、C/C跨平台的原理 三、网络互连模型 四、客户端与服务器 五、计算机之间的通信基础 1、IP地址与MAC地址 2、ARP与ICMP对比 ①ARP协议&#xff08;地址解析协议&#xff09; ②ICMP协议&#xff08…

云原生俱乐部-k8s知识点归纳(1)

这篇文章主要是讲讲k8s中的知识点归纳&#xff0c;以帮助理解。虽然平时也做笔记和总结&#xff0c;但是就将内容复制过来不太好&#xff0c;而且我比较喜欢打字。因此知识点归纳总结还是以叙述的口吻来说说&#xff0c;并结合我的理解加以论述。k8s和docker首先讲一讲docker和…

基于Node.js+Express的电商管理平台的设计与实现/基于vue的网上购物商城的设计与实现/基于Node.js+Express的在线销售系统

基于Node.jsExpress的电商管理平台的设计与实现/基于vue的网上购物商城的设计与实现/基于Node.jsExpress的在线销售系统

Git 对象存储:理解底层原理,实现高效排错与存储优化

### 探秘 Git 对象存储&#xff1a;底层原理与优化实践#### 一、Git 对象存储的底层原理 Git 采用**内容寻址文件系统**&#xff0c;核心机制如下&#xff1a; 1. **对象类型与存储** - **Blob 对象**&#xff1a;存储文件内容&#xff0c;通过 git hash-object 生成唯一 SHA-…

【2025CVPR-目标检测方向】RaCFormer:通过基于查询的雷达-相机融合实现高质量的 3D 目标检测

1. 研究背景与动机​ ​问题​:现有雷达-相机融合方法依赖BEV特征融合,但相机图像到BEV的转换因深度估计不准确导致特征错位;雷达BEV特征稀疏,相机BEV特征因深度误差存在畸变。 ​核心思路​:提出跨视角查询融合框架,通过对象查询(object queries)同时采样图像视角(原…

【每日一题】Day 7

560.和为K的子数组 题目&#xff1a; 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续非空序列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,1], k 2 输出&#xff1a;2 示例 2&#x…

3ds MAX文件/贴图名称乱码?6大根源及解决方案

在3ds MAX渲染阶段&#xff0c;文件或贴图名称乱码导致渲染失败&#xff0c;是困扰众多用户的常见难题。其背后原因多样&#xff0c;精准定位方能高效解决&#xff1a;乱码核心根源剖析字符编码冲突 (最常见)非ASCII字符风险&#xff1a; 文件路径或名称包含中文、日文、韩文等…

链路聚合路由器OpenMPTCProuter源码编译与运行

0.前言 前面写了两篇关于MPTCP的文章&#xff1a; 《链路聚合技术——多路径传输Multipath TCP(MPTCP)快速实践》《使用MPTCPBBR进行数据传输&#xff0c;让网络又快又稳》 对MPTCP有了基本的了解与实践&#xff0c;并在虚拟的网络拓扑中实现了链路带宽的叠加。 1.OpenMPTC…

AI时代企业转型指南:用AI降本增效,销售转化翻3倍,获客成本砍一半!

AI时代&#xff0c;大部分企业每天都在问同一个问题&#xff1a;AI到底能帮我做什么&#xff1f;无论你是做电商、做IP、做操盘手&#xff0c;还是传统企业老板&#xff0c;你都会发现一个现实——AI真正的用途是用来在业务场景里直接降本增效的。对我个人来说&#xff0c;AI已…