博客主页:【夜泉_ly】
本文专栏:【C++】
欢迎点赞👍收藏⭐关注❤️
文章目录
- 💡前言
- 1.构造函数
- 1.1函数原型
- 1.2用法速览
- 1.3详解
- () -重点⭐
- ( s) -重点⭐
- ( n, c) -重点⭐
- ( str) -重点⭐
- ( str, pos, len)
- ( s, n)
- 2.容量函数
- 2.1函数原型
- 2.2用法速览
- 2.3详解
- size() -重点⭐
- reserve() -重点⭐
- resize() -重点⭐
💡前言
在这篇文章中,我将简单探讨 C++
中 string
的基本用法。写这篇文章的主要目的是帮助我巩固所学的知识,同时也便于未来的复习和参考。
如果你想大致的了解string
的基本用法,也可以留下来看看。
对于那些希望深入学习更多细节的读者,可以去看看这个网站:cplusplus.com,以获取更全面的参考资料。
1.构造函数
1.1函数原型
在 C++98 中,string
类提供了七种构造函数,具体的函数原型如下:
string();
string (const string& str);
string (const string& str, size_t pos, size_t len = npos);
string (const char* s);
string (const char* s, size_t n);
string (size_t n, char c);
template <class InputIterator> string (InputIterator first, InputIterator last);
注:
函数原型中的缺省参数npos是一个为静态成员变量,类型为size_t,值为-1(整型的最大值),通常用于表示无效位置。
1.2用法速览
在众多构造函数中,以下四个是最常用的:
string s1; // 创建一个空的 string 对象
string s2("Hello World"); // 用 C 字符串初始化 string 对象
string s3(6, '6'); // 用 6 个字符 '6' 填充 string 对象
string s4(s2); // 使用 s2 拷贝构造一个新的 string 对象
此外,还有一些不太常用的构造函数:
string s5(s2, 6, 5); // 从 s2 的第 7 个字符(下标为6)开始,拷贝 5 个字符
string s6("Hello World", 5); // 用"Hello World"的前 5 个字符初始化 string 对象
至于其他构造方式,我暂时还不熟悉。
1.3详解
() -重点⭐
string s1;
调用的是默认构造函数,创造出一个空的string
类对象,其字符串为空,即""
;
VS上的调试图:
在调试中可以看到,空的string
类对象具有以下特征:
- 初始字符串长度
size
为0
- 初始容量
capacity
为15
(实际为16
,但最后一个用来放\0
)
尽管空的 string
类对象可以被打印出来,但不会显示任何内容:
cout << "s1:" << s1 << endl;
cout << "s1:" << s1 << 'a' << endl;
( s) -重点⭐
string s2("Hello World");
以C类型的字符串构造string
类对象。
C类型的字符串就是以
\0
为结束标志的字符串。
VS上的调试图:
显然,这种构造方式有一个限制,即它无法将'\0'
填入string
类对象,例如:
string s2("Hello \0World"); // 此时 '\0' 之后的字符不会被存储
( n, c) -重点⭐
string s3(6, '6');
用n
个c
字符填充string
类对象。
与用C类型的字符串构造不同,这种构造方式可以将'\0'
填入string
类对象:
string s3(10,'\0'); // 创建一个长度为 10 的字符串,全部填充 '\0'
( str) -重点⭐
string s2("Hello World");
string s4(s2);
调用的是拷贝构造函数。
会拷贝原string
类对象的所有内容,而不是简单地拷贝 C 类型的字符串:
string s3(10,'\0'); // 创建一个长度为 10 的字符串,全部填充 '\0'
string s4(s3); // 拷贝构造,复制 s3 的内容
( str, pos, len)
string s2("Hello World");
string s5(s2, 6, 5);
从str
的pos
位置(指下标)开始,拷贝len
个字符。
string (const string& str, size_t pos, size_t len = npos);
在其函数原型中,len
给了一个缺省参数npos
。
static const size_t npos = -1;
可以看见,npos
为一个静态常变量,值为-1
,又因为其类型为size_t
,因此,npos
实际值为整型的最大值。
当传参时,不传len
或传的len
过大,会默认从str
的pos
位置拷贝至结尾。
string s2("Hello World");
string s5_1(s2, 6); // 默认拷贝至字符串结束
string s5_2(s2, 6, 100); // 尝试拷贝超过实际长度,将从第 6 个字符拷贝至结束
( s, n)
拷贝字符串s
的前n
个字符。感觉比较废。
2.容量函数
2.1函数原型
size: 返回当前字符串的长度
size_t size() const;
length: 返回当前字符串的长度(与 size() 等效)
size_t length() const;
max_size: 返回字符串可以容纳的最大字符数
size_t max_size() const;
resize: 改变字符串的长度
void resize (size_t n);
void resize (size_t n, char c);
capacity: 返回当前字符串的容量
size_t capacity() const;
reserve: 预留空间
void reserve (size_t n = 0);
clear: 清空字符串
void clear();
empty: 判断字符串是否为空
bool empty() const;
shrink_to_fit: 收缩容量
void shrink_to_fit();
2.2用法速览
常用的:
string s; // 创建一个空字符串
//size:
cout << s.size() << endl; // 输出字符串的长度
//reserve:
s.reserve(100); // 预留 100 个字符的空间
cout << s.capacity() << endl; // 输出当前字符串的容量
//resize:
//1:
s.resize(5); // 调整字符串的长度为 5
cout << s.size() << endl; // 输出调整后的长度
//2:
s.resize(10,'6'); // 调整字符串的长度为 10,填充字符 '6'
cout << s << endl; // 输出字符串内容
//clear:
s.clear(); // 清空字符串内容
cout << s << endl; // 输出空字符串
不常用的:
//length:
cout << s.length() << endl; // 输出字符串的长度(与 size() 等效)
//max_size:
cout << s.max_size() << endl; // 输出字符串可以容纳的最大字符数
//capacity:
cout << s.capacity() << endl; // 输出当前字符串的容量
//empty:
cout << (s.empty() ? "String is empty" : "String is not empty") << endl; // 判断字符串是否为空
//shrink_to_fit:
s.shrink_to_fit(); // 收缩字符串的容量,释放多余的内存
cout << s.capacity() << endl; // 输出收缩后的字符串容量
2.3详解
size() -重点⭐
重点,但用法很简单。
可以返回字符串的有效字符长度。
注,并非C语言类型的字符串的长度:
string s(10,'0'); // 创建一个包含 10 个 '0' 的字符串
cout << s.size() << endl; // 输出结果将是 10
Output:
10
注意事项:
string
类对象的size
并不一定等于其容量capacity
。- 由于不知道编码方式,返回值可能不等于实际编码后的字符数量。
std::size
与std::length
的用法完全一致(一般用size
),文档中将它们称作同义词synonym
。
reserve() -重点⭐
注意区分reserve和reverse!!!
可以改变string
类对象的容量capacity
, 不会改变有效数据个数size!!!
扩容与缩容:
可简单测试一下:
string s;
size_t cap = s.capacity();
cout << "Init:" << cap << endl;
for (int i = 0; i < 200; i++)
{
s += ' ';
if(cap != s.capacity())
{
cap = s.capacity();
cout << "capacity change : " << cap << endl;
}
}
不同环境的自动扩容机制不同,VS大概是1.5倍,linux大概是2倍。
用reserve
手动扩容,如果提前知道要多少空间,这样做可减少自动扩容带来的消耗:
string s;
s.reserve(100);
VS上,一般会稍微大于你给的值,因为它有一套自己的对齐规则。
这样的实现并没有问题:
如果字符串有内容,一般不会缩容:
string s("1");
s.reserve(100);
s.reserve(0);
cout << s.capacity() << endl;
如果为空字符串(初始为空/被clear清空),可能会缩容:
string s;
s.reserve(100);
s.reserve(0);
cout << s.capacity() << endl;
string s2("Hello World!!!!!!!!!!!!!!!!");
s2.clear();
s2.reserve(0);
cout << s2.capacity() << endl;
reserve
也有缺省参数:
void reserve (size_t n = 0);
因此: s . r e s e r v e ( ) ; s.reserve(); s.reserve(); 等价于 s . r e s e r v e ( 0 ) ; s.reserve(0); s.reserve(0);
如果有编译器,任何情况它都不缩/缩,这种实现也是对的,因为这是一个不具有约束力的请求:
resize() -重点⭐
可以改变string
类对象的有效数据个数size
,增容时可能改变容量capacity
大小。
该函数提供了两个重载版本:
void resize (size_t n);
void resize (size_t n, char c);
用法:
-
string s; s.resize(3,'6'); // 将字符串大小调整为 3,并用字符 '6' 填充
当使用
void resize (size_t n, char c);
增加size
时,多余的部分用字符c
填充。 -
s.resize(6); // 调整大小为 6,多余部分将用 '\0' 填充
当使用void resize (size_t n);
增加size
时,多余部分默认用'\0'
填充:
所以std::resize
的两个重载函数应该可以合成为一个:void resize (size_t n, char c = '\0');
,没这样做的原因好像是函数重载比缺省参数早出现。 -
s.resize(0);
直接变成空串,但不改变
capacity
。
这点感觉和std::clear
完全相似。
希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!
本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!