string容器
C风格字符串(以空字符结尾的字符数组)太过复杂难于掌握,不适合大程序的开发,所以C++标准库定义了一种string类,可变长字符串的处理类,定义在头文件<string>
。
string和c风格字符串对比
char *
是一个指针,String是一个类 string封装了char *
,管理这个字符串,是一个char *
型的容器。- string封装了很多实用的成员方法
查找find,拷贝copy,删除delete 替换replace,插入insert - 不用考虑内存释放和越界 string管理
char *
所分配的内存。每一次string的复制,取值都由string类负责维护,不用担心复制越界和取值越界等。
string容器常用操作
构造函数
cpp
// 创建一个空的字符串 例如: string str;
string();
// 使用一个string对象初始化另一个string对象
string(const string& str);
// 使用字符串s初始化
string(const char* s);
// 使用n个字符c初始化
string(int n, char c);
基本赋值操作
cpp
// char*类型字符串 赋值给当前的字符串
string& operator=(const char* s);
// 把字符串s赋给当前的字符串
string& operator=(const string &s);
// 字符赋值给当前的字符串
string& operator=(char c);
// 把字符串s赋给当前的字符串
string& assign(const char *s);
// 把字符串s的前n个字符赋给当前的字符串
string& assign(const char *s, int n);
// 把字符串s赋给当前字符串
string& assign(const string &s);
// 用n个字符c赋给当前字符串
string& assign(int n, char c);
// 将s从start开始n个字符赋值给字符串
string& assign(const string &s, int start, int n);
存取字符操作
cpp
// 通过[]方式取字符
char& operator[](int n);
// 通过at方法获取字符
char& at(int n);
拼接操作
cpp
// 重载+=操作符,拼接另一个string字符串
string& operator+=(const string& str);
// 重载+=操作符,拼接另一个C风格字符串
string& operator+=(const char* str);
// 重载+=操作符,拼接一个字符
string& operator+=(const char c);
// 把字符串s连接到当前字符串结尾
string& append(const char *s);
// 把字符串s的前n个字符连接到当前字符串结尾
string& append(const char *s, int n);
// 同operator+=()
string& append(const string &s);
// 把字符串s中从pos开始的n个字符连接到当前字符串结尾
string& append(const string &s, int pos, int n);
// 在当前字符串结尾添加n个字符c
string& append(int n, char c);
查找和替换
cpp
// 查找str第一次出现位置,从pos开始查找
int find(const string& str, int pos = 0) const;
// 查找s第一次出现位置,从pos开始查找
int find(const char* s, int pos = 0) const;
// 从pos位置查找s的前n个字符第一次位置
int find(const char* s, int pos, int n) const;
// 查找字符c第一次出现位置
int find(const char c, int pos = 0) const;
// 查找str最后一次位置,从pos开始查找
int rfind(const string& str, int pos = npos) const;
// 查找s最后一次出现位置,从pos开始查找
int rfind(const char* s, int pos = npos) const;
// 从pos查找s的前n个字符最后一次位置
int rfind(const char* s, int pos, int n) const;
// 查找字符c最后一次出现位置
int rfind(const char c, int pos = 0) const;
// 替换从pos开始n个字符为字符串str
string& replace(int pos, int n, const string& str);
// 替换从pos开始的n个字符为字符串s
string& replace(int pos, int n, const char* s);
比较操作
cpp
/*
compare函数在>时返回 1,<时返回 -1,==时返回 0。
比较区分大小写,比较时参考字典顺序,排越前面的越小。
大写的A比小写的a小。
*/
// 与字符串s比较
int compare(const string &s) const;
// 与字符串s比较
int compare(const char *s) const;
string子串
cpp
// 返回由pos开始的n个字符组成的字符串
string substr(int pos = 0, int n = npos) const;
插入和删除操作
cpp
// 插入字符串
string& insert(int pos, const char* s);
// 插入字符串
string& insert(int pos, const string& str);
// 在指定位置插入n个字符c
string& insert(int pos, int n, char c);
// 删除从Pos开始的n个字符
string& erase(int pos, int n = npos);
string和C风格字符串转换
cpp
// string 转 char*
string str = "itcast";
const char* cstr = str.c_str();
// char* 转 string
char* s = "itcast";
string str(s);
Tips
在C++中存在一个从const char *
到string的隐式类型转换,却不存在从一个string对象到C_string的自动类型转换。对于string类型的字符串,可以通过c_str()
函数返回string对象对应的c_string。
通常,。
为了修改string字符串的内容,下标操作符[]
和at
都会返回字符的引用。但当字符串的内存被重新分配之后,可能发生错误。
cpp
string s = "abcdefg";
char& a = s[2];
char& b = s[3];
a = '1';
b = '2';
cout << s << endl;
cout << (int *)s.c_str() << endl;
s = "pppppppppppppppppppppppp";
//a = '1';
//b = '2';
cout << s << endl;
cout << (int *)s.c_str() << endl;
小练习:写一个函数,函数内部将string字符串中的所有小写字母都变为大写字母。
cpp
#include <iostream>
#include <string>
using namespace std;
void upperstr(string& str) {
for (auto& item : str) {
item = toupper(item);
}
}
int main(int argc, char *argv[]) {
string str("I love china");
cout << str << endl;
upperstr(str);
cout << str << endl;
return 0;
}
数字转换
C++标准库提供了相关类对字符串和数字进行转换。
字符串流类sstream用于string的转换。
使用字符串流类需要包含#include <sstream>
头文件。
istringstream表示字符串输入流,ostringstream表示字符串输出流。
cpp
#include <iostream>
#include <sstream>
using namespace std;
int main(int argc, char *argv[])
{
//字符串转换为数字
istringstream iss("3.14");
double pi;
if(iss >> pi)
{
cout << "PI = " << pi<< endl;
}
//数字转换为字符串
ostringstream oss;
if(oss << pi)
{
string PI = oss.str();
cout << "PI = " << PI << endl;
}
return 0;
}
手动实现string
基础实现
cpp
#pragma once
#include <cstring>
#include <iostream>
#include <istream>
#include <ostream>
class MyString {
public:
MyString() : _str(new char[1]) {
_str[0] = '\0';
}
~MyString() {
delete[] _str;
}
MyString(const char *str) {
const int len = strlen(str);
_str = new char[len + 1];
strcpy(_str, str);
_str[len] = '\0';
}
// 成员函数使用了堆空间 实现拷贝构造和赋值重载以避免浅拷贝
MyString(const MyString &s) {
_str = new char[s.size()];
strcpy(_str, s._str);
}
MyString &operator=(const MyString &s) {
if (&s == this) {
return *this;
}
delete[] _str;
_str = new char[s.size()];
strcpy(_str, s._str);
return *this;
}
// 移动构造
MyString(MyString &&s) noexcept : _str(std::move(s._str)) {
s._str = nullptr;
}
// 移动赋值运算符重载
MyString &operator=(MyString &&s) noexcept {
if (&s == this) {
return *this;
}
delete[] _str;
this->_str = s._str;
s._str = nullptr;
return *this;
}
// 重载运算符
bool operator>(const MyString &rhs) const {
return strcmp(_str, rhs._str) > 0;
}
bool operator<(const MyString &rhs) const {
return strcmp(_str, rhs._str) < 0;
}
bool operator==(const MyString &rhs) const {
return strcmp(_str, rhs._str) == 0;
}
char &operator[](int index) {
if (index < 0 || index >= size()) {
throw std::out_of_range("index error");
}
return _str[index];
}
const char &operator[](int index) const {
if (index < 0 || index >= size()) {
throw std::out_of_range("index error");
}
return _str[index];
}
// API
int size() const { return strlen(_str); }
const char *c_str() const {
return _str;
}
private:
// 重载输出运算符
friend std::ostream &operator<<(std::ostream &out, const MyString &s);
// 重载+运算符
friend MyString operator+(const MyString &lhs, const MyString &rhs);
private:
char *_str;
};
std::ostream &operator<<(std::ostream &out, const MyString &s) {
std::cout << s._str;
return out;
}
MyString operator+(const MyString &lhs, const MyString &rhs) {
char *newStr = new char[strlen(lhs._str) + strlen(rhs._str) + 1];
strcpy(newStr, lhs._str);
strcat(newStr, rhs._str);
// 1.一定要构造临时对象然后释放newStr,否则造成内存泄漏
MyString temp(newStr);
delete[] newStr;
// 2.使用移动语义来优化
return std::move(temp);
}
cpp
#include "my-string.h"
int main() {
MyString s1("zhangsan");
std::cout << s1 << std::endl;
std::cout << s1.size() << std::endl;
MyString s2 = s1;
std::cout << s2 << std::endl;
std::cout << s2.size() << std::endl;
MyString s3(std::move(s1));
std::cout << s3 << std::endl;
std::cout << s3.size() << std::endl;
MyString s4 = "lisi";
std::cout << s4 << std::endl;
std::cout << s4.size() << std::endl;
MyString s5 = s3 + s4;
std::cout << s5 << std::endl;
std::cout << s5.size() << std::endl;
MyString s6 = "hello" + s4;
std::cout << s6 << std::endl;
std::cout << s6.size() << std::endl;
return 0;
}
bash
zhangsan
8
zhangsan
8
zhangsan
8
lisi
4
zhangsanlisi
12
hellolisi
9
添加迭代器
cpp
#pragma once
#include <cstring>
#include <iostream>
#include <istream>
#include <ostream>
class MyString {
public:
MyString() : _str(new char[1]) {
_str[0] = '\0';
}
~MyString() {
delete[] _str;
}
MyString(const char *str) {
const int len = strlen(str);
_str = new char[len + 1];
strcpy(_str, str);
_str[len] = '\0';
}
// 成员函数使用了堆空间 实现拷贝构造和赋值重载以避免浅拷贝
MyString(const MyString &s) {
_str = new char[s.size()];
strcpy(_str, s._str);
}
MyString &operator=(const MyString &s) {
if (&s == this) {
return *this;
}
delete[] _str;
_str = new char[s.size()];
strcpy(_str, s._str);
return *this;
}
// 移动构造
MyString(MyString &&s) noexcept : _str(std::move(s._str)) {
s._str = nullptr;
}
// 移动赋值运算符重载
MyString &operator=(MyString &&s) noexcept {
if (&s == this) {
return *this;
}
delete[] _str;
this->_str = s._str;
s._str = nullptr;
return *this;
}
// 重载运算符
bool operator>(const MyString &rhs) const {
return strcmp(_str, rhs._str) > 0;
}
bool operator<(const MyString &rhs) const {
return strcmp(_str, rhs._str) < 0;
}
bool operator==(const MyString &rhs) const {
return strcmp(_str, rhs._str) == 0;
}
char &operator[](int index) {
if (index < 0 || index >= size()) {
throw std::out_of_range("index error");
}
return _str[index];
}
const char &operator[](int index) const {
if (index < 0 || index >= size()) {
throw std::out_of_range("index error");
}
return _str[index];
}
// API
int size() const { return strlen(_str); }
const char *c_str() const {
return _str;
}
// ✨ 自定义迭代器
// 1.迭代器类实现
class iterator {
public:
iterator(char *p = nullptr) : _pi(p) {}
bool operator!=(const iterator &it) { return _pi != it._pi; }
void operator++() { ++_pi; } // 迭代器遍历时一般都使用前置++,注意返回值
char *operator++(int) { return _pi++; }
char &operator*() { return *_pi; }
private:
char *_pi;
};
// 2.提供begin和end函数
iterator begin() { return iterator(_str); }
iterator end() { return iterator(_str + size()); }
private:
// 重载输出运算符
friend std::ostream &operator<<(std::ostream &out, const MyString &s);
// 重载+运算符
friend MyString operator+(const MyString &lhs, const MyString &rhs);
private:
char *_str;
};
std::ostream &operator<<(std::ostream &out, const MyString &s) {
std::cout << s._str;
return out;
}
MyString operator+(const MyString &lhs, const MyString &rhs) {
char *newStr = new char[strlen(lhs._str) + strlen(rhs._str) + 1];
strcpy(newStr, lhs._str);
strcat(newStr, rhs._str);
// 1.一定要构造临时对象然后释放newStr,否则造成内存泄漏
MyString temp(newStr);
delete[] newStr;
// 2.使用移动语义来优化
return std::move(temp);
}
cpp
#include "my-string.h"
int main() {
MyString s1("zhangsan");
std::cout << s1 << std::endl;
std::cout << s1.size() << std::endl;
for (auto it = s1.begin(); it != s1.end(); ++it) {
std::cout << *it;
}
std::cout << std::endl;
for (auto it = s1.begin(); it != s1.end(); it++) {
std::cout << *it;
}
std::cout << std::endl;
for (auto it : s1) {
std::cout << it;
}
std::cout << std::endl;
return 0;
}
bash
zhangsan
8
zhangsan
zhangsan
zhangsan