string
string简介
String 类
string是表示字符序列的对象, 与普通的char*不一样,本质上是一个类,内部封装了各种函数
。
标准字符串类通过类似于标准字节容器的接口为此类对象提供支持,但添加了专门设计用于处理单字节字符字符串的功能。
字符串类是 basic_string 类模板的实例化
,它使用 char(即字节)作为其字符类型,以及其默认的 char_traits 和分配器类型(有关模板的更多信息,请参阅basic_string)。
请注意,此类处理字节与所使用的编码无关:如果用于处理多字节或可变长度字符(如 UTF-8)的序列,则此类的所有成员(如长度或大小)及其迭代器仍将根据字节(而不是实际编码字符)进行操作。
成员类型
value_type | char |
traits_type | char_traits |
allocator_type | allocator |
reference | char& |
const_reference | const char& |
pointer | char* |
const_pointer | const char* |
iterator | a random access iterator to char (convertible to const_iterator) |
const_iterator | a random access iterator to const char |
reverse_iterator | reverse_iterator |
const_reverse_iterator | reverse_iterator<const_iterator> |
difference_type | ptrdiff_t |
size_type | size_t |
迭代器(Iterator)
迭代器(Iterator)是一种设计模式,用于提供一种方法来访问容器(如数组、集合、列表等)中的元素,而不暴露其底层表示。在C++中,迭代器被广泛应用于标准库中的各种容器,如 vector、list、map 等,以及其他数据结构。它们提供了一种统一的方式来遍历容器中的元素,并允许对容器中的元素进行读写操作。
迭代器的原理:
迭代器的类型名是iterator,本质上是一个类,也可以看作一个指针么,使用迭代器要加上域名比如string::iterator。
调用迭代器的函数为 .begin()返回相当于头指针的迭代器, .end()返回相当于指向尾部元素下一个位置的指针,这个不可访问数据,用来当终止条件。
迭代器实际上是一种指向容器元素的对象,它允许逐个访问容器的元素,并且支持对容器元素的操作。迭代器一般提供以下几种操作:
解引用(Dereferencing): 通过迭代器访问容器中的元素值。
递增(Incrementing): 将迭代器指向下一个元素。
比较(Comparing): 比较两个迭代器的位置关系,例如判断是否相等或大小关系。
迭代器的实现原理基于面向对象的设计,通过重载运算符来实现上述操作,例如 * 运算符用于解引用,++ 运算符用于递增迭代器。
注意事项:
失效问题: 在使用迭代器遍历容器时,若容器发生了增删操作,可能会导致迭代器失效(invalidated)。失效的迭代器不能再被使用,因此在进行插入或删除操作后,通常需要更新迭代器或者小心使用。
范围控制: 确保迭代器在有效的范围内操作,避免越界访问容器。
可变性问题: 不同类型的迭代器可能对容器的可变性有不同的影响。例如,使用 const_iterator 可以确保不修改容器元素。
迭代器类型: 不同的容器可能提供不同类型的迭代器,如正向迭代器、逆向迭代器等,应根据需要选择合适的迭代器类型。
示例:
以下是一个简单的示例,演示如何使用迭代器遍历和修改一个 vector 容器中的元素:
#include <iostream>
#include <vector>
using namespace std;
int main() {
string s = "123456789";
// 使用迭代器遍历并输出元素
cout << "Original vector: ";
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// 使用迭代器修改元素
for (string::iterator it = numbers.begin(); it != numbers.end(); ++it) {
*it *= 2;
}
// 再次遍历输出修改后的元素
cout << "Modified vector: ";
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
cout << *it << " ";
}
cout << endl;
return 0;
}
在这个例子中,我们使用 vector 的迭代器 begin() 和 end() 来遍历容器中的元素,并且通过解引用迭代器 *it 来修改元素的值。这个例子展示了迭代器的基本用法和注意事项。
自动类型推导auto
自动类型推导(Automatic Type Deduction)是C++11引入的特性,通过关键字 auto 来实现。它允许编译器根据变量的初始化表达式推断出其类型,而无需显式地指定类型名。这种特性的引入使得代码更加简洁和易于维护,尤其是在处理复杂的模板类型时尤为有用。
使用场景和语法
在以下几种情况下,可以使用 auto 进行类型推导:
变量声明和初始化:
auto x = 5; // 推导为 int
auto pi = 3.14; // 推导为 double
auto name = "John"; // 推导为 const char*
在这些示例中,编译器会根据右侧的初始化表达式推断出变量的类型。
函数返回类型推导(C++14):
auto add(int a, int b) {
return a + b; // 推导为 int
}
函数的返回类型可以使用 auto,编译器会根据函数体中的返回语句推断返回类型。
迭代器和泛型编程:
std::vector<int> numbers = {
1, 2, 3, 4, 5};
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
// *it 推断为 int&
std::cout << *it << " ";
}
在迭代器等泛型编程中,使用 auto 可以简化代码,特别是当容器的类型较复杂或是使用了模板时。
注意事项
类型推导的准确性: auto 推导的类型与初始化表达式密切相关,例如 auto x = 5; 推导为 int,而 auto y = 5.0; 推导为 double。
可读性和维护性: 尽管 auto 可以简化代码,但有时显式指定类型可以增强代码的可读性和维护性,特别是在函数签名和复杂表达式中。
初始化表达式的重要性: 类型推导依赖于变量的初始化,如果初始化表达式改变,推导出的类型也会相应改变。
避免过度使用: 在代码可读性受影响或需要强调类型时,应避免过度使用 auto。
示例
下面是一个结合了多种情况的示例:
#include <iostream>
#include <vector>
int main() {
auto num = 10; // 推导为 int
auto name = "Alice"; // 推导为 const char*
std::vector<double> values = {
1.2, 3.4, 5.6};
for (auto it = values.begin(); it != values.end(); ++it) {
// *it 推导为 double&
std::cout << *it << " ";
}
std::cout << std::endl;
auto sum = [](int a, int b) {
return a + b; }; // 推导为 int
return 0;
}
string中的常用函数
构造函数
在c++11中总共有这些个构造函数:
default (1) | string(); |
copy (2) | string (const string& str); |
substring (3) | string (const string& str, size_t pos, size_t len = npos); |
from c-string (4) | string (const char* s); |
from buffer (5) | string (const char* s, size_t n); |
fill (6) | string (size_t n, char c); |
range (7) | template string (InputIterator first, InputIterator last); |
initializer list (8) | string (initializer_list il); |
move (9) | string (string&& str) noexcept; |
npos小知识
npos 是C++标准库中的一个静态成员常量,通常用于表示字符串和容器类中的无效或末尾位置。
具体来说:
- 对于 std::string 类型,npos 是 std::string::npos,它表示字符串的末尾位置或者无效位置。
- 对于 std::vector、std::deque 等容器,也有类似的静态成员常量表示末尾或无效位置。
在实际使用中,npos 的值通常是一个很大的正整数,用来表示找不到或者无效的位置。例如,对于 std::string,npos 的值通常是 std::string::npos,这个值在大多数实现中是 -1,但是它是无符号数,因此实际的二进制值是 std::string::npos。
使用案例
默认构造函数
cpp
std::string str; // 创建一个空字符串
这种形式的构造函数创建一个空的字符串对象。
字符串字面值和长度构造函数
cpp
const char* cstr = “Hello”;
std::string str(cstr); // 使用C风格字符串初始化
std::string str(cstr, 3); // 使用部分C风格字符串初始化
这种构造函数接受一个 C 风格字符串 const char* 或者一个部分C风格字符串和长度,用于初始化 std::string 对象。
复制构造函数
std::string str1("Hello");
std::string str2(str1); // 使用另一个字符串对象初始化
这种构造函数使用同类型的另一个 std::string 对象来初始化新的对象。
重复字符构造函数
std::string str(5, 'A'); // 创建包含5个'A'字符的字符串
这种构造函数创建一个由指定数量重复字符组成的字符串。
移动构造函数(C++11及以后)
std::string str1 = "Hello";
std::string str2 = std::move(str1); // 使用移动语义来初始化str1 可能被置为空
这种构造函数使用移动语义来初始化一个新的 std::string 对象,通常用于转移资源所有权,提高性能