做出网站,微信公众号做视频网站,wordpress资讯站,网站多网合一前言 本文主要介绍STL六大组件中的容器之一#xff1a;string#xff0c;在学习C的过程中#xff0c;我们要将C视为一个语言联邦#xff08;摘录于Effective C条款一#xff09;。如何理解这句话呢#xff0c;我们学习C#xff0c;可将其分为四个板块#xff1b;分别为…前言 本文主要介绍STL六大组件中的容器之一string在学习C的过程中我们要将C视为一个语言联邦摘录于Effective C条款一。如何理解这句话呢我们学习C可将其分为四个板块分别为C、Object-Oriented C面向对象的C、Template C模板、STL。本文就介绍STL中的string C语言字符串的局限性
C语言中的字符串处理依赖于字符数组和一系列标准库函数如strcpy, strcat, strlen等。这些方法虽然基本能满足需求但存在几个显著的缺点 安全性问题C语言中的字符串操作常常设计直接的内存操作问题荣誉造成缓冲区的溢出、内存泄漏的等安全问题例如strcpy函数在赋值数据时不会检查目标缓冲区的大小这可能导致超出其容量的写操作。效率低下C语言的字符串操作通常需要手动管理字符串长度和内存如需拼接字符串时肯需要多次调用strlen来获取当前长度然后再添加新内容这样的操作效率较低。使用不便C语言的字符串需要程序员对内存非常小心谨慎的操作容易出错且代码难以维护。 Cstring类的优势
与C语言的基本字符串处理相比C的string类提供了一个更安全、更高效、更易用的字符串操作方式 自动内存管理string类自动管理内存使用者不需要关心内存分配和释放的问题极大地降低了内存泄漏和缓冲区溢出的风险。丰富的成员函数string类内置了大量的成员函数如append, find, replace, substr等使得字符串的处理更为方便和直观。动态大小string对象可以根据需要动态增长和缩小无需预先声明最大容量这一点与静态大小的C语言字符数组形成鲜明对比。操作符重载和迭代器支持string类重载了多个操作符如、等支持迭代器使得字符串操作更符合C的对象操作习惯也支持现代C中的范围for循环和算法库函数。兼容性和灵活性尽管string以字节为单位进行操作对于多字节字符集的支持可能有限但它在大多数情况下能够兼容处理UTF-8等编码尤其是在使用支持这些编码的库时。 string类 C 中提供了专门的头文件string注意不是 string.h这个是C风格字符串相关函数的头文件来支持string类型。string类定义隐藏了字符串的数组性质让我们可以像处理普通变量那样处理字符串。string对象和字符数组之间的主要区别是可以将string对象声明为简单变量而不是数组 标准库中的string类文档介绍 1.字符串时表示字符序列的类。 2.标准的字符串类提供了对此类对象的支持其接口累死与标准字符容器的接口但添加了专门用于操作单字节字符字符串的设计特性。 3.string类是使用char即作文他的字符类型使用他的默认char_traits和分配器类型 4.string类是basic_string模版类的一个实例它使用char来实例化basic_string模板类并用char_traits和allocator作文basic_string分默认参数根于更多漫步信息参考basic_string。 5.注意这个类独立于所使用的编码来处理字节如果用来处理多字节或变长字符符UTF-8的序列这个类的缩影成员如长度或大小已经它的迭代器将仍然按照字节而不是实际编码的字符来操作。 字符串作为字符序列 string类本质上是一个用于表示字符序列的容器。它封装了字符数组的许多复杂操作提供了一种更安全、更直观的方式来处理文本数据。用户不需要关心底层的字符数组和内存管理所有这些都由string类自动处理。 接口与标准容器 string类的接口设计借鉴了标准库中STL容器的模式例如vector和deque。这意味着string提供了类似于这些容器的多种成员函数如迭代器的支持、元素访问、修改操作、容量查询等。这种设计使得string类即熟悉又易于使用对于已经熟悉其他C标准容器的开发者。 string类的常用接口说明
string类对象的常见构造
(constructor)函数名称功能说明string()构造空的string类对象即空字符串string(const char* s)用C-string来构造string类对象string(size_t n,char c)string类对象中包含n个字符cstring(const string s)拷贝构造函数
string s1; // 构造空的string类对象s1
string s2(hello bit); // 用C格式字符串构造string类对象s2
string s3(s2); // 拷贝构造s3
string类对象的容量操作
函数名称功能说明size返回字符串有效字符长度length返回字符串有效字符长度capacity返回空间总大小empty检查字符串释放为空串是返回true否返回falseclear清空有效字符reserve为字符串预留空间resize将有效字符的个数改成n个多出的空间用字符c填充
size()和length()
size_t size() const;
size_t length() const;
std::string str Hello, world!;
std::cout Length of string: str.length() std::endl; // 输出 13
std::cout Size of string: str.size() std::endl; // 同样输出 13 这两个函数都返回字符串当前的长度即字符串中字符的数量这两种并无相异忧郁string出现的较早原因当时没有STL其他容器先出现了length后来为了统一接口与其他容器接口保持一致因此出现了size。 capacity()
size_t capacity() const; std::string str Test;
std::cout Capacity of string: str.capacity() std::endl; // 输出分配的内存大小 capacity()函数返回为字符串分配的存储空间的大小通常这个值大于或等于size()。这可以给出底层数组的空间大小有助了解内存的使用情况。 empty
bool empty() const;
std::string str;
std::cout Is the string empty? (str.empty() ? Yes : No) std::endl; // 输出 Yes empty()函数检查字符串是否为空即长度为0。如果字符串为空返回true否则返回false。 clear
void clear() noexcept;
std::string str Something;
str.clear(); // 清空字符串
std::cout String after clear: str std::endl; // 输出 clear()函数删除字符串中的所有字符使其长度变为0这个函数不会改变容量的大小。 reserve
void reserve(size_t n 0);
std::string str;
str.reserve(100); // 为存储至少100个字符预分配内存
std::cout New capacity after reserve: str.capacity() std::endl; reserve()函数试图改变字符串的容量即预先分配足够的内存来存储至少n个字符避免多次增加字符串大小时的重复分配。 resize
void resize(size_t n);
void resize(size_t n, char c);
std::string str Hello;
str.resize(10, x); // Helloxxxxx
str.resize(3); // Hel resize()函数更改字符串的长度如果新的长度大于当前长度新位置将于字符c填充如果提供了c的话。如果新的长度小于当前长度多余的字符将被截断空间大小不会被改变。 string类的访问及遍历操作 在std::string类中访问和遍历字符串的方法包括使用下标操作符、迭代器和范围for循环。这些方法提供了灵活的方式来访问和遍历字符串中的字符。 函数名称功能说明operator[]返回pos位置的字符const string类对象调用begin endbegin获取一个字符的迭代器end获取最后一个字符下一个位置的迭代器rbegin rend反向迭代器rbegin获取一个字符的迭代器rend获取最后一个字符下一个位置的迭代器范围forC11支持更简介的范围for的新遍历方式
operator[]
std::string str Hello, world!;
char ch1 str[0]; // 访问第一个字符ch1 H
char ch2 str[7]; // 访问第八个字符ch2 wstd::cout First character: ch1 std::endl;
std::cout Eighth character: ch2 std::endl;// 修改字符串中的字符
str[5] !;
std::cout Modified string: str std::endl; // 输出 Hello! world! 下标操作符operator[]允许你访问字符串中的特定位置的字符这种访问方式是随机访问时间复杂度为O(1)。 迭代器
std::string str Hello;
std::cout Characters in string: ;// 使用正向迭代器遍历字符串
for (auto it str.begin(); it ! str.end(); it) {std::cout *it ;
}
std::cout std::endl;// 使用反向迭代器遍历字符串
std::cout Characters in reverse: ;
for (auto rit str.rbegin(); rit ! str.rend(); rit) {std::cout *rit ;
}
std::cout std::endl; 迭代器提供了一种方式按顺序访问容器中的每个元素。std::string支持正向和反向迭代器 范围for
std::string str world;std::cout Characters in string using range-for: ;
for (char c : str) {std::cout c ;
}
std::cout std::endl;// 修改字符串中的字符需要使用引用
for (char c : str) {c toupper(c); // 将字符转换为大写
}
std::cout Modified string: str std::endl; // 输出 WORLD 范围for循环是C11引入的一个特性它允许简介的变量容器中的所有元素在使用范围for循环遍历std::string时可以直接访问每个字符。 string类对象的修改操作 类提供了多种方法来修改字符串的内容这些方法包括添加、删除、插入和替换字符等操作下面这些方法的详细介绍和使用实例。 函数名称功能说明push_back在字符串后尾插入字符cappend在字符串后追加一个字符串operator在字符串后追加字符串strc_str返回c格式字符串find npos从字符串pos位置开始往后开始找字符c返回该字符在字符串中的位置rfind从字符串pos位置开始往后开始找字符c返回该字符在字符串中的位置substr在str中从pos位置开始截取n个字符然后将其返回
push_back
void push_back(char c);
std::string str Hello;
str.push_back(!);
std::cout str std::endl; // 输出 Hello! push_back将一个字符追加到字符串的末尾。 append
string append(const string str);
string append(const string str, size_t subpos, size_t sublen);
string append(const char* s);
string append(const char* s, size_t n);
string append(size_t n, char c);
std::string str Hello;
str.append( world, 6); // 附加了 world 中的前6个字符
std::cout str std::endl; // 输出 Hello world append方法添加字符串、字符数组或多个相同字符到现有字符串的末尾。 operator
void operator(const string str);
void operator(char c);
void operator(const char* s);
std::string str Hello;
str , world!;
std::cout str std::endl; // 输出 Hello, world! operator用于将字符串、字符或者c字符串附加到现有的std::string对象上。 c_str
int main()
{string s1(hello world);cout s1.c_str() endl;cout s1.data() endl;return 0;
} c_str返回string类中存储字符串的字符指针在C语言中字符串是以\0结尾的一些字符的集合。但在C中string类不以\0结尾而是根据有效空间的大小结束。本质作用是将const string* 类型转化为const char* 类型。 find和npos
// 从string拷贝给字符数组
// size_t copy (char* s, size_t len, size_t pos 0) const;
char arr[] hello world;
string s1(xxxxxxxxxxxxxxxx);
s1.copy(arr, 6, 2);
cout s1 endl;
// 寻找某个字符串的起始位置
// size_t find (const string str, size_t pos 0) const;
string tmp(abc);
string s2(abbadabcdeabcd);
size_t pos1 s2.find(tmp, 0);
// 从后往前找
//size_t rfind (const string str, size_t pos npos) const;
size_t pos2 s2.rfind(tmp, s2.size() - 1);
cout pos1 endl;
cout pos2 endl; find有很多函数重载学会一种就可以了size_t find(const string s, int pos 0);s是需要查找的字符串pos是从那个位置查找如果未找到则返回nposnpos表示size_t 的最大值。 rfind
string s1(fghasdabc);
cout s1.rfind(a) endl;string s2(abc);
cout s1.rfind(s2) endl;
cout s1.rfind(e) endl; rfind和find功能是一样的不过一个是往前找一个向后找的区别。 substr
string s1(Hello World);cout s1 endl;
string s2(s1.substr(0, 5));cout s1 endl;
cout s2 endl; 在string中的pos位置截取n个字符然后返回。 string类的深拷贝与浅拷贝问题
浅拷贝 在类中如果用户没有显示实现而是由编译器自动生成的成员函数叫做默认成员函数这样的成员函数有六个。默认成员函数中的拷贝构造函数和赋值运算符重载函数会以逐字节的方式将原对象的内容原封不动的拷贝或赋值给新的对象如果对象中管理资源最后就会导致多个对象共用一份资源当其中一个对象销毁时会将该资源释放掉其他对象再想操作该资源时就会发生访问违规这便是浅拷贝。 namespace qx
{class string{public:string(const char* s){if (nullptr s){cout string():false endl;return;}char* ptr new char[strlen(s) 1];strcpy(_str, s);}//........private:char* _str;};
}int main()
{qx::string s1(hello world!);qx::string s2(s1);return 0;
} 由于类对象销毁时调用析构函数会先将s2对象销毁再调用析构进行销毁s1对象但是因为拷贝构造是浅拷贝导致s1和s2指向同一块内存空间销毁掉s2之后内存空间被释放s1找不到改内存空间最终违规访问。 深拷贝 如果一个类中涉及到资源管理其拷贝构造函数、赋值运算符重载以及析构函数都必须要显示给出一般情况都是按照深拷贝方式提供。 //显示实现拷贝构造
string(const string str):_str(nullptr)
{string strTmp(str._str);swap(_str, strTmp._str);
}//显示实现赋值运算符重载
string operator(string str)
{if (this ! str){string strTmp(str);swap(_str, strTmp._str);}
}//显示实现析构函数
~string()
{if (_str){delete[] _str;_str nullptr;}
} 深拷贝即给每个对象独立分配资源保证多个对象之间不会因为共享资源而造成多次释放导致程序崩溃的问题。 string总结 深入探讨C标准库中的string类从其构造函数、容量操作函数到访问及遍历操作以及类对象的修改操作。string类作为C中处理字符串的核心工具提供了丰富的接口来高效、安全地管理和操作字符串。掌握这些功能不仅可以提高编程效率还能帮助开发者编写更加健壮和可维护的代码有效地处理现代软件开发中的文本数据挑战。