持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
string
c++大大增强了对字符串的支持,除了可以使用C风格的字符串,还可以使用内置的 string 类。string 类处理起字符串来会方便很多,完全可以代替C语言中的字符数组或字符串指针
string 是 C++ 中常用的一个类,使用 string 类需要包含头文件,下面的例子介绍了几种定义 string 变量(对象)的方法:
转换为C风格的字符串
虽然 C++ 提供了 string 类来替代C语言中的字符串,但是在实际编程中,有时候必须要使用C风格的字符串(例如打开文件时的路径),为此,string 类为我们提供了一个转换函数 c_str(),该函数能够将 string 字符串转换为C风格的字符串,并返回该字符串的 const 指针(const char*)
string 字符串的增删改查
s1 = s2 = "1234567890";
s3 = "aaa";
s1.insert(5, s3); //12345aaa67890
//pos 表示要插入的位置,也就是下标;str 表示要插入的字符串,它可以是 string 字符串
string s1, s2, s3;
s1 = s2 = s3 = "1234567890";
s2.erase(5); //12345
s3.erase(5, 3); //1234590 3表示len
string s1 = "first second third";
string s2;
s2 = s1.substr(6, 6); //second 6表示len
string s1 = "first second second third";
string s2 = "c";
s1.find(s2); //8
s1.find(s2,10); //15 从第十个位置开始找
s1.rfind(s2); //15 //从右边开始找
s1.find(s2,10); //8
返回的值都是数组下标
string 特性
在c++中每个string是一个独立拷贝,当s1赋值给s2时,s2的数据就拥有自己的内存地址。当data内容改变时,也是在那个内存地址上改变,不会影响s1的值。
#include <iostream>
#include <string>
using namespace std;
int main() {
string s1{"hello world"};
string s2 = s1;
cout<<"s2 before heap address: "<<s2<<" "<<reinterpret_cast<uintptr_t>(s2.data())<<endl;
string s3 = s1;
string s4{"hello world"};
s2[0] = 'H';
cout<<"s1 heap address:"<<s1<<" "<<reinterpret_cast<uintptr_t>(s1.data())<<endl;
cout<<"s2 after heap address: "<<s2<<" "<<reinterpret_cast<uintptr_t>(s2.data())<<endl;
cout<<"s3 heap address: "<<s3<<" "<<reinterpret_cast<uintptr_t>(s3.data())<<endl;
cout<<"s4 heap address: "<<s4<<" "<<reinterpret_cast<uintptr_t>(s4.data())<<endl;
}
/*
s2 before heap address: hello world 140722732664864
s1 heap address:hello world 140722732664832
s2 after heap address: Hello world 140722732664864
s3 heap address: hello world 140722732664896
s4 heap address: hello world 140722732664928
*/
在java中有个常量池的概念,
在java中每个string是一个引用,当s1赋值给s3时,s3拥有和s1相同的内存地址。当s3的内容改变时,会在常量池里创建一个新的常量"runoob",s3并指向这个常量地址。
String s1 = "Runoob"; // String 直接创建
String s2 = "Runoob"; // String 直接创建
String s3 = s1; // 相同引用
String s4 = s1;
String s5 = new String("Runoob"); // String 对象创建
s3 = "runoob";
System.out.println(s1+" "+s1.getClass()+"@"+s1.hashCode());
System.out.println(s2+" "+s2.getClass()+"@"+s2.hashCode());
System.out.println(s3+" "+s3.getClass()+"@"+s3.hashCode());
System.out.println(s4+" "+s4.getClass()+"@"+s4.hashCode());
System.out.println(s5+" "+s5.getClass()+"@"+s5.hashCode());
/*
Runoob class java.lang.String@-1835937737
Runoob class java.lang.String@-1835937737
runoob class java.lang.String@-919804905
Runoob class java.lang.String@-1835937737
Runoob class java.lang.String@-1835937737
*/
string作为函数参数
为了高效利用内存,避免拷贝,使用const引用,当传入的参数为string类型时,没有问题,string不会被拷贝,但如果传入的参数是字符数组或字符指针,那么它相当于新建一个string拷贝再传进去,所以data的地址发生变化
#include <iostream>
#include <string>
using namespace std;
void fun(const string& s){
cout<<"fun: "<<s<<" "<<reinterpret_cast<uintptr_t>(s.data())<<endl;
}
int main() {
string s = "hello world";
cout<<"main: "<<s<<" "<<reinterpret_cast<uintptr_t>(s.data())<<endl;
fun(s);
char data[] = "very long string..";
cout<<"main: "<<data<<" "<<reinterpret_cast<uintptr_t>(data)<<endl;
fun(data);//fun(string(data))
return 0;
}
/*
main: hello world 140729198891808
fun: hello world 140729198891808
main: very long string.. 140729198891856
fun: very long string.. 94022293754560
*/
string_view
不拥有自己的data,只有一个指针和长度
当s1赋值给sv1时,是一个o1操作,只是把s1的指针地址给他
g++ -std=c++17 pipetest.cpp
#include <iostream>
#include <string>
#include <string_view>
using namespace std;
int main() {
string s1{"hello world"};
string_view sv1 = s1;
string_view sv2 = sv1;
cout<<"s1 stack address: "<<s1<<" "<<&s1<<endl;
cout<<"sv1 stack address: "<<sv1<<" "<<&sv1<<endl;
cout<<"sv2 stack address: "<<sv2<<" "<<&sv2<<endl;
cout<<"s1 stack 十进制 address: "<<s1<<" "<<reinterpret_cast<uintptr_t>(&s1)<<endl;
cout<<"sv1 stack 十进制 address: "<<sv1<<" "<<reinterpret_cast<uintptr_t>(&sv1)<<endl;
cout<<"sv1 stack 十进制 address: "<<sv2<<" "<<reinterpret_cast<uintptr_t>(&sv2)<<endl;
cout<<"s1 heap address:"<<s1<<" "<<reinterpret_cast<uintptr_t>(s1.data())<<endl;
cout<<"sv1 heap address: "<<sv1<<" "<<reinterpret_cast<uintptr_t>(sv1.data())<<endl;
cout<<"sv2 heap address: "<<sv2<<" "<<reinterpret_cast<uintptr_t>(sv2.data())<<endl;
return 0;
}
/*
s1 stack address: hello world 0x7ffc035d37a0
sv1 stack address: hello world 0x7ffc035d3780
sv2 stack address: hello world 0x7ffc035d3790
s1 stack 十进制 address: hello world 140720364926880
sv1 stack 十进制 address: hello world 140720364926848
sv1 stack 十进制 address: hello world 140720364926864
s1 heap address:hello world 140720364926896
sv1 heap address: hello world 140720364926896
sv2 heap address: hello world 140720364926896
*/
&s1输出的是16进制地址,reinterpret_cast<uintptr_t>(&s1)输出的是十进制地址。
cout << reinterpret_cast<uintptr_t>(&str) << endl;
cout << reinterpret_cast<uintptr_t>(str.data()) << endl;
前面是str对象(类实例)本身的地址,在栈上。后面是str对象data字段(字符串数据)的地址,在堆上。
string_view作为函数参数
#include <iostream>
#include <string>
#include <string_view>
using namespace std;
void fun2(string_view s){
cout<<"fun: "<<s<<" "<<reinterpret_cast<uintptr_t>(s.data())<<endl;
}
int main() {
string s = "hello world";
cout<<"main: "<<s<<" "<<reinterpret_cast<uintptr_t>(s.data())<<endl;
fun2(s);
string_view sv = s;
cout<<"main: "<<sv<<" "<<reinterpret_cast<uintptr_t>(sv.data())<<endl;
fun2(sv);
char data[] = "very long string..";
cout<<"main: "<<data<<" "<<reinterpret_cast<uintptr_t>(data)<<endl;
fun2(data);//fun(string(data))
return 0;
}
/*
main: hello world 140720538463360
fun: hello world 140720538463360
main: hello world 140720538463360
fun: hello world 140720538463360
main: very long string.. 140720538463376
fun: very long string.. 140720538463376
*/
缺点
缺点:不能append,不能拼接,确保在string_view的生命周期内,string_view不被改变
abcd被改写位123\0
#include <iostream>
#include <string>
#include <string_view>
using namespace std;
int main() {
string s = "abcdefg";
string_view sv = s;
s = "123";
cout<<"main: "<<sv<<endl;
return 0;
}
/*
main: 123efg
*/
#include <iostream>
#include <string>
#include <string_view>
using namespace std;
void fun2(string_view s){
cout<<"fun: "<<s<<" "<<reinterpret_cast<uintptr_t>(s.data())<<endl;
}
int main() {
string s = "abcd";
string_view sv = s;
s = "123456";
s = "H";
cout<<"main: "<<sv<<endl;
return 0;
}
/*
main: H34
*/
\