string类对象的常见构造:
如何初始化类的对象是由类本身决定的。一个类可以定义很多种构造函数。当然每种构造函数在在初始值得数量或者类型都有差别(函数重载)。
string() 构造空的string类对象,即空字符串
string(const char* s) 用C-string来构造string类对象
string(size_t n, char c) string类对象中包含n个字符c
string(const string&s) 拷贝构造函
string s0;//默认初始化,s0是一个空字符串
string s1("hello world");//用字面值"hello world",构造s1
string s2(s1);//用s1拷贝构造 s2
string s3 = s2;//等价于s3(s2),用s2拷贝构造s3
//先用字面值"hello world"构造一个string类型的临时对象,再用临时对象拷贝构造s4.
string s4 = "hello world";
string s5(10,'c');//将s5初始化为,由10个字符'c'组成的串
读写string对象:
(1)用IO操作符(<<,>>)读写string对象
我们可以使用标准库的iostream来读写int,double,等内置类型。也可以在string类里边通过运算符重载的方式,用IO操作符(<<,>>)读写string对象。
int main(){
string s; //空字符串
cin >> s;//将string 对象读入s遇到空白停止
cout << s << endl;//输出s
retrun 0;
}
需要注意的是:在文件读取时,string类对象会忽略所有的空白(空格符,换行符,制表符等),从的一个真正的字符开始到空白结束。
所以程序输入的如果是" Hello world".输出的将会是"Hello"。
在运算符>> 和 << 重载时函数的返回值是运算符左侧的操作对象。所以所以多个输入或者输出可以连在一起:
int main(){
sting s1,s2;//定义两个空字符串
cin >> s1 >> s2;// 把第一个串读到s1中,第二个读到s2中
cout << s1 << s2 << endl;//输出
}
如果这次依然输入" Hello world" 输出为“Helloword“
(2)使用getline读取一整行:
istream& getline (istream& is, string& str);
有时候我们希望等在最终得到的字符串中,保留空白符。这时应该用getline函数代替>>运算符。函数从输入流is中读入内容到str中,直到遇到换行为止。geline只要一遇到换行符就结束读取操作并返回结果。如果一开始就输入换行符,那么就会得到一个空的sting对象。
getline也会返回他的流参数,因此可以使用getline的结果作为条件:
int main(){
string s0;//定义一个空串
//读入未知个string对象,每次读入一整行,直到文件的结束
while( getline(s0) )
cout<< s0 << endl;
return 0;
}
关于容量的操作:
size_t size() const;返回字符串有效字符长度。
size_t length() const;返回字符串有效字符长度
size_t capacity() const; 返回空间总大小
bool empty() const; 检测字符串释放为空串,是返回true,否则返回false
void clear(); 清空有效字符 ,clear仅在逻辑层面进行数据清理,而不会进行内存释放。
void reserve (size_t n = 0);为字符串预留空间。
void resize (size_t n); void resize (size_t n, char c);将有效字符的个数该成n个,多出的空间用字符c填充
需要注意的是:
-
size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size()。
-
clear()只是将string中有效字符清空,不改变底层空间大小,只是将对象的_size置为0。
-
resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。操作后string对象有效元素的多少改变成了n。
-
reserve(size_t res_arg=0):为容器预留足够的空间,避免不必要的重复分配。不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,容器不会发生任何改变。如果大于将底层空间,则扩容。而对象有效元素的多少始终不发生改变。
关于size_t 和 size_type: -
标准定义中,size_t和size_type被定义为unsigned int。但是sizt_t是C++的一个标准的typedef,全局有效,定义在全局名称空间中,size_type是STL定义的,size_t不是容器概念,而size_type是容器概念,没有容器不能使用。
-
size_t的引入增强了程序在不同平台上的可移植性。经测试发现,在32位系统中size_t是4字节的,而在64位系统中,size_t是8字节的,这样利用该类型可以增强程序的可移植性。
处理string对象中的字符:
(1)使用范围for处理对象的每个字符;
c++11新标准提供了一种语句:范围for语句。这种语句遍历给定序列的每个元素
string str("Hello world");
for (auto a : str )
cout << c << endl;//输出当前字符,并换行
范围for把变量a 和 序列 str联系起来。auto是让编译器确定a的类型,a的类型是char。每次循环将序列的下一个字符拷贝给变量a,然后输出。但是如果在范围for中修改str序列,上边变量的定义显然是不行的,因为每次循环都只是将str的字符拷贝一份给变量a。所以如果要改变序列str,就应该把循环变量定义成引用类型:
string str("Hello world");
for (auto &a : str )
a = 'H';//将str序列的每个字符换成'H'
cout << str << endl;
(2)用下标运算符[],处理string对象某个字符:
string s0 = "hello";
for(size_t i = 0; i < s0.size(); i++){
cout << s0[i];
}
cout << endl;
string对象的下标必须大于等于0,而小于s.size()。使用超出此范围的下标将引发不可预知的结果,以此推断,使用下标访问空string会引发不可预知的结果。
(3)用迭代器处理string对象的某个字符:
- 最常见的迭代器:
string s = "hello";
string::iterator it = s.begin();
while( it != s.end() ){
cout<< *it <<" ";
++it;
}
cout << endl;
需要注意的是,s.begin()是‘h’,但是s,end()是最后一个 字符’o’的下一个位置。
- 反向迭代器:
string s = "hello";
string :: reverse_iterator it = s.rbegin();
while( it != s.rend() ){
cout<< *it <<" ";
++it;
}
cout << endl;
程序会输出: o l l e h
- const迭代器;
const string s = "hello";
string :: const_iterator it = s.begin();
while( it != s.end() ){
cout<< *it <<" ";
++it;
}
cout << endl;
string类对象的修改操作
(1)尾插一个字符
void push_back (char c);在字符串后尾插字符c
(2)尾插一个字符串。
string& append (const string& str);//str的所有内容,拼接到对象的后边
string& append (const string& str, size_t subpos, size_t sublen);//str字符串从subpos开始共sublen位拼接到对象后边。
string& append (const char* s);//将c格式的字符串拼接到,对象的后边。
string& append (const char* s, size_t n);//字符串的前 n、位拼接到对象后边。
string& append (size_t n, char c);// 将5个字符c拼接到,对象后边。
(3)在字符串后追加字符串str
string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);
(4)返回C格式字符串
const char* c_str() const noexcept;
c_str()函数返回一个指向正规C字符串的指针常量, 内容与本string串相同。这是为了与c语言兼容,在c语言中没有string类型,故必须通过string类对象的成员函数c_str()把string 对象转换成c中的字符串样式。
注意:一定要使用strcpy()函数 等来操作方法c_str()返回的指针
(5)从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
size_t find (const string& str, size_t pos = 0) const; //查找对象–string类对象
size_t find (const char* s, size_t pos = 0) const; //查找对象–字符串
size_t find (const char* s, size_t pos, size_t n) const; //查找对象–字符串的前n个字符
size_t find (char c, size_t pos = 0) const; //查找对象–字符
找到 – 返回 第一个字符的索引,没找到–返回 string::npos(代表 -1 表示不存在)
(6)在str中从pos位置开始,截取n个字符,然后将其返回
string substr (size_t pos = 0, size_t len = npos) const;
模拟实现string类
(1)实现一个简单的string类包括 构造 拷贝构造 赋值运算符重载 析构 reserve(szie_t) push_back(char) 等
namespace LZH{
struct string{
string(char* str = ""){//构造
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);//strcpy 会连'\0' 一起拷贝
}
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){//拷贝构造
string tem(s._str);
swap(tem);
}
~string(){//析构
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
string& operator=(string s){//赋值运算符重载 先调用拷贝构造初始化形参
swap( s );
return *this;
}
void reserve(size_t newcapacity){
if (newcapacity <= _capacity){
return;
}
char* newstr = new char[newcapacity + 1];
strcpy(newstr, _str);
delete[] _str;
_capacity = newcapacity;
_str = newstr;
}
void push_back(char c){
if (_size == _capacity){//扩容
int newcapacity = _capacity == 0 ? 1 : 2 * _capacity;
reserve(newcapacity);
}
_str[_size++] = c;
_str[_size] = '\0';
}
private:
size_t _size;
size_t _capacity;
char* _str;
static size_t npos;
};
size_t string :: npos = -1;
}//namespace LZH
(2)具体实现:
https://github.com/WhynotHH/Simulate-STL/blob/master/string.h