1 C++ 标准库
1.1 重载左移操作符
我们先来说一下重载左移操作符,下面看一段有趣的代码
// 21-1.cpp
#include<stdio.h>
const char endl = '\n';
class Console
{
public:
Console& operator << (int i)
{
printf("%d", i);
return *this;
}
Console& operator << (char c)
{
printf("%c", c);
return *this;
}
Console& operator << (const char* s)
{
printf("%s", s);
return *this;
}
Console& operator << (double d)
{
printf("%f", d);
return *this;
}
};
Console cout;
int main()
{
cout << 1 <<endl;
cout << "Hello" <<endl;
double a = 0.1;
double b = 0.2;
cout << a + b << endl;
return 0;
}
我们定义了一个类 Console,类中重载了左移操作符 <<,定义了一个全局对象 cout,通过宏定义常量 cout,这样可以在不调用 printf 的情况下完成打印功能。
编译运行结果如下:
$ g++ 21-1.cpp -o 21-1
$ ./21-1
1
Hello
0.300000
其实上面这些,C++标准库库中已经给出了实现
1.2 C++标准库
- C++ 标准库由类库和函数库组成,不是 C++语言的一部分
- C++标准库中定义的类和对象位于 std 命名空间中,涵盖了 C 库的功能
- C++ 标准库的头文件没有 .h 后缀
C++ 编译环境包括C++ 的标准语法模块,编译器厂商扩展的语法模块,C++ 的标准库,比如 <string>,C 语言的兼容库,比如 <cstring>,同时 C++ 完全兼容 C 语言。
C++ 标准库预定于了多数常用的数据结构,如下所示:
2 C++ 中的字符串类
C 语言不支持真正意义上的字符串,用字符数组和一组函数实现字符串操作。
C++ 没有原生的字符串类型,但是可以自定义类型,通过类完成字符串类型的定义,C++标准库提供了 string 类型,string 支持字符串连接,字符串大小比较,字符串查找与提取,字符串的插入与替换。
下面看一下字符串类的使用实验
// 21-2.cpp
#include<iostream>
#include<string>
using namespace std;
void string_sort(string a[], int n) // 冒泡排序
{
for (int i = n-1; i >= 0; i--)
{
bool flag = false;
for (int j = 0; j < i; j++)
{
if (a[j] > a[j+1])
{
swap(a[j], a[j+1]);
flag = true;
}
}
if (flag == false) break;
}
}
string string_add(string a[], int n)
{
string ret = "";
for (int i = 0; i < n; i++)
{
ret += a[i] + "; ";
}
return ret;
}
int main()
{
string sa[6] = {"Hello", "C#", "Java", "C++", "C", "Python"};
string_sort(sa, 6);
for (int i = 0; i < 6; i++)
{
cout << sa[i] << endl;
}
cout << endl;
cout << string_add(sa, 6) << endl;
return 0;
}
C++ 中的字符串支持直接用大于号,小于号按照字典序进行大小比较,加号 “+” 可以连接字符串。
编译运行:
$ g++ 21-2.cpp -o 21-2
skx@ubuntu:~/c++$ ./21-2
C
C#
C++
Hello
Java
Python
C; C#; C++; Hello; Java; Python;
2.1 字符串与数字的转换
字符串流类(sstream)用于对字符串和数字进行转换,头文件为<sstream>
- istringstream:字符串输入流
- ostringstream:字符串输出流
编程实验:字符串和数字的转换
// 21-3.cpp
#include<iostream>
#include<sstream>
#include<string>
using namespace std;
#define TO_NUMBER(s, n) (istringstream(s) >> n)
#define TO_STRING(n) (((ostringstream&)(ostringstream() << n)).str())
int main()
{
double n = 0;
if (TO_NUMBER("123.45", n))
{
cout << n << endl;
}
string s = TO_STRING(12345);
cout << s << endl;
return 0;
}
2.2 字符串循环右移
例如:abcdefg 右移 3 位得到的 efgabcd
// 21-4.cpp
#include<iostream>
#include<string>
using namespace std;
string operator >> (const string& s, unsigned 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;
}
int main()
{
string s = "abcdefg";
cout << (s >> 3) << endl;
return 0;
}
s.substr(pos, n)
解释:返回一个string,包含 s 中从 pos 开始的 n 个字符的拷贝(pos 的默认值是0,n 的默认值是 s.size() - pos,即不加参数会默认拷贝整个 s)
3 不要混用C++ 和 C 中的字符串
C++ 中的字符串是类对象,C 中的字符串是字符数组,二者混用会出问题。
// 21-5.cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
const char* p = "12345";
string s = "";
s.reserve(10);
for (int i = 0; i < 5; i++)
{
s[i] = p[i];
}
cout << s << endl;
return 0;
}
void reserve( size_type size = 0 );
函数reserve()将字符串的容量设置为至少 size。
编译运行:
$ g++ 21-6.cpp -o 21-6
$ ./21-6
我们明明将字符串 p 中的字符拷贝进字符串 s 了,为什么打印出的字符串为空呢?
原因如下:
虽然字符已经拷贝进字符串 s 中,但是 s 是类的对象,类中的 m_length 一直为 0,所以虽然已经拷贝了字符,编译器依然认为该字符串空,如果两个都是 string 对象,使用赋值运算符直接拷贝就不会出现这样的错误。
4 小结
1、C++ 中没有直接支持原生的字符串类型,通过 string 类支持字符串概念
2、string 类支持字符串和数字的相互转换
3、string 类通过一个成员变量保存字符串长度,通过数据空间保存字符数据,避免 C++ 与 C 字符串混用