【从 C 向 C++ 进阶】- 类 - 14. string 类

1. string 类

在 C 中,对于字符串的实现采用的是字符数组的方式,并非真正意义上的字符串。因此,C++ 标准库提供了 string 类来实现真正意义上的字符串。

string 具备以下特性:

  • 支持字符串直接连接,即具备 strcat 函数功能。
  • 支持字符串的大小直接比较,即具备 strcmp 函数功能。
  • 支持子串直接查找和提取,即具备 strstr 函数功能。
  • 支持字符串的插入和替换。
  • 支持直接获取字符串长度,即具备 strlen 函数功能。
  • 实验:
int main(int argc, char* argv[])
{
	string str_A = "abc";
	string str_B = "def";   
	cout << "str_A = " << "\"" << str_A << "\"" << endl;                            // str_A = "abc"
	cout << "str_B = " << "\"" << str_B << "\"" << endl;                            // str_B = "def"
	
	/* 字符串比较 */
	cout << "(str_A == str_B) = " << (str_A == str_B) << endl;                      // (str_A == str_B) = 0
	
	/* 字符串拼接 */
	string str_C = str_A + str_B;
	cout << "str_C = " << "\"" << str_C << "\"" << endl;                            // str_C = "abcdef"
	str_C += str_A;
	cout << "str_C = " << "\"" << str_C << "\"" << endl;                            // str_C = "abcdefabc"
	
	/* 计算字符串长度 */
	cout << "str_C.lenght() = " << str_C.length () << endl;                         // str_C.lenght() = 9
	
	/* 提取子串 */
	cout << "str_C.substr(3) = " << "\"" << str_C.substr(3) << "\"" << endl;        // str_C.substr(3) = "defabc"
	cout << "str_C.substr(1,4) = " << "\"" << str_C.substr(1,4) << "\"" << endl;    // str_C.substr(1,4) = "bcde"
	
	/* 查找子串 */
	cout << "str_C.find(\"b\") = " << str_C.find("b") << endl;                      // str_C.find("b") = 1
	return 0;
}

2. 字符串与数字相互转换

C++ 标准库提供了相关类用于字符串与数字的相互转换。istringstream 表示字符串输入流,ostringstream 表示字符串输出流。使用字符串流类需要包含 #include <sstream> 头文件。

  • 实验:
int main(int argc, char* argv[])
{
    /* 字符串转换为数字 */
    istringstream iss("3.14");
    float flt_pi = 0;
    if (iss >> flt_pi)
    {
        cout << "flt_pi = " << flt_pi<< endl;					// flt_pi = 3.14
    }
	
    /* 数字转换为字符串 */
    ostringstream oss;
    if (oss << flt_pi)
    {
        string str_pi = oss.str();
        cout << "str_pi = " << "\"" << str_pi << "\"" << endl;	// str_pi = "3.14"
    }
    return 0;
}

3. 字符串的循环移动

对于 C++ 的 string 字符串对象,可以重载运算符 “<<” 和 “>>” ,通过 string 类提供的功能组合模拟类似于二进制的位移,实现字符串的循环移动。

  • 实验:
string operator >> (const string& s, int n)
{
    string ret;
    unsigned int pos = 0;
	
    // 右移位数计算
    n = n % s.length();
	
    // 右移的开始位置	
    pos = s.length() - n;

    // 提取右移部分的子串作为起始
    ret = s.substr(pos);
	
    // 拼接子串
    ret += s.substr(0, pos);
    return ret;
}

string operator << (const string& s, int n)
{
    string ret;
    unsigned int pos = n;
	
    // 左移位数计算
    pos = pos % s.length();
    
	// 提取左移部分的子串作为结尾
	ret = s.substr(0, pos);
    
	// 拼接子串
    ret = s.substr(pos) + ret;
	
    return ret;
}

int main(int argc, char *argv[])
{
    string str_A = "abcdef";
	cout << "str_A = " << "\"" << str_A << "\"" << endl;                // str_A = "abcdef"
    cout << "str_A >> 1 = " << "\"" << (str_A >> 1) << "\"" << endl;	// str_A >> 1 = "fabcde"
	
    string str_B = "abcdef";
	cout << "str_B = " << "\"" << str_B << "\"" << endl;                // str_B = "abcdef"
    cout << "str_B << 1 = " << "\"" << (str_B << 1) << "\"" << endl;	// str_B << 1 = "bcdefa"	
    return 0;
}

4. C++ 方式打印字符型指针的内容

在 C 中,用 printf 函数打印字符型指针内容即指针指向的地址时,使用 %p 便可以对指向字符型指针内容进行打印。在 C++ 中,直接用输出流 cout 输出指向字符型指针时,会发现打印的是指针指向的字符串,并非指针本身的内容。

这是由于 C++ 标准库 中 I/O 类对运算符 << 进行重载,因此在遇到字符型指针时会将其当作字符串名来处理,输出指针所指的字符串。想要打印字符型指针的内容,需要通过强制类型转换,可以用 static_cast 把字符型指针转换成无类型指针。

  • 实验:
int main()
{
	char* p = "123456";
	
	/* C 方式打印 */
	printf("%p\n", p);								// 0x400a74
	printf("%s\n", p);								// 123456
	
	/* C++ 方式打印 */
	cout << p << endl;								// 123456
	cout << static_cast<const void*>(p) << endl;	// 0x400a74
 
    return 0;
}

5. string 类的底层实现

string 类通过重载运算符 [] 支持以数组的方式访问字符串,

  • 示例:
string str = "123456";
cout << "str[1] = " << "\"" << str[1] << "\"" << endl;  // str[1] = "2"

那么 string 类的底层是怎么维护字符串对象的呢?其实在创建 string 对象时,会申请至少初始字符串长度的内存空间,并用一个成员变量记录字符串长度,这个成员变量用 length() 获得。

但假如如果使用数组的方式把字符添加到超出原字符串的内容空间,此时实际并没有真正地添加到有效的字符串后,记录字符串长度的成员变量并没有更新,因此切勿使用数组的方式进行字符串的拼接,对字符串的操作也应尽量调用 string 类提供的功能函数。

使用运算符 + 或调用函数 append() 进行字符串的拼接时,会查看拼接进来的字符串的长度是否大于已申请的内存空间,如果小于,则直接把该字符串存放到原字符串的结尾并更新字符串长度;否则,会申请一段大于拼接后的字符串的长度的内存空间,然后分别把原字符串与拼接字符串拼接存放到新的内存空间去,原内存空间会被释放。

下面通过使用 c_str() 获取 string 对象的字符串首地址来看看这一更换内存的过程。

  • 实验:
int main(int argc, char *argv[])
{
	string str = "12";
	const char* p_A = str.c_str();
	cout << "*p_A = " << "\"" << p_A << "\"" << endl;           // *p_A = "12"
	cout << "p_A = " << static_cast<const void*>(p_A) << endl;  // p_A = 0x7ffebe9d8390
	
	str += "3456789987654321";
	const char* p_B = str.c_str();              
	cout << "*p_B = " << "\"" << p_B << "\"" << endl;           // *p_B = "123456789987654321"
	cout << "p_B = " << static_cast<const void*>(p_B) << endl;  // p_B = 0x1713030
	
	str += "123456789987654321";
	const char* p_C = str.c_str();
	cout << "*p_C = " << "\"" << p_C << "\"" << endl;           // *p_C = "123456789987654321123456789987654321"
	cout << "p_C = " << static_cast<const void*>(p_C) << endl;  // p_C = 0x1713060
	
	cout << "*p_A = " << "\"" << p_A << "\"" << endl;           // *p_A = "<"
	cout << "*p_B = " << "\"" << p_B << "\"" << endl;           // *p_B = ""
	
	return 0;
}

更多从 C 向 C++ 进阶系列博文

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值