C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合面向对象的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
因此C++中,为了让我们更简单、方便、快捷的使用字符串类型,C++提供了string类。
string类文档介绍
string类在使用的时候我们需要包含头文件 #include ,以及using namespace std;
总结:
1. string是表示字符串的字符串类。
2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
3. string在底层实际是:basic_string模板类的别名,
typedef basic_string
4. 不能操作多字节或者变长字符的序列。
(constructor)函数名称 | 功能说明 |
---|---|
string() | 构造空的string类对象,即空字符串 |
string(const char* s) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) | 拷贝构造函数 |
int main()
{
string s1; // 构造空的string类对象s1
string s2("hello"); // 用C格式字符串构造string类对象s2
string s3(s2); // 拷贝构造s3
return 0;
}
namespace s
{
class string
{
public:
//string()
// :_str(new char[1]{ '\0' })//这里开一个空间,并赋值为\0,如果直接赋值为nullptr的话
// ,_size(0) //在实例化一个无参对象后,对对象进行操作的时候会引发空指针异常
// ,_capacity(0)
//{}
//全缺省构造函数
string(const char* str = "")//常量字符串默认以\0结尾,因此不需要给
: _size(strlen(str))
, _capacity(_size)
{
_str = new char[_capacity + 1];//多开一个空间,需要存放\0
strcpy(_str, str);//将字符串拷贝到_str
}
private:
char* _str;
size_t _size;//有效存储字符个数
size_t _capacity;//实际空间
};
}
这里我们用一个全缺省将无参构造与构造都包括了。
这个构造函数很简单,这里就不多讲了,大家应该可以看懂。
传统写法:
//拷贝构造
string(const string& s)
: _size(strlen(s._str))
, _capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
现代写法:
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
//现代写法 s1(s2)
string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);//构造
swap(tmp);//this->swap(s);
}
库里提供了交换函数,我们封装一个交换函数,内部的实现就使用库里面的swap,我们拷贝构造函数里面先调用构造函数,实例化一个tmp,再将tmp与this交换。
传统写法:
string& operator=(const string& s)
{
if (this != &s)
{
string tmp(s);//拷贝构造
swap(tmp);
}
return *this;
}
现代写法:这里与拷贝构造的现代写法是类似的,不再多说。
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
string& operator=(const string& s)
{
if (this != &s)
{
string tmp(s);//拷贝构造
swap(tmp);
}
return *this;
}
我们其实可以对现代写法继续精简一下,我们这里参数是引用传参,我们不用引用就会产生一次拷贝,对拷贝的形参直接进行交换,这样就可以简化写法,但是这里的效率其实是一样的,都是一次拷贝构造 + 一次交换。
// 极致写法,效率与上面一样
string& operator=(string tmp)//参数直接调用拷贝构造
{
swap(tmp);
return *this;
}
#include
#include
using namespace std;
namespace s
{
class string
{
public:
//string()
// :_str(new char[1]{ '\0' })//这里开一个空间,并赋值为\0,如果直接赋值为nullptr的话
// ,_size(0) //在实例化一个无参对象后,对对象进行操作的时候会引发空指针异常
// ,_capacity(0)
//{}
//全缺省构造函数
string(const char* str = "")//常量字符串默认以\0结尾,因此不需要给
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];//多开一个空间,需要存放\0
strcpy(_str, str);//将字符串拷贝到_str
}
//拷贝构造
// 传统写法
//string(const string& s)
// :_size(s._size)
// ,_capacity(s._capacity)
//{
// _str = new char[s._capacity + 1];
// strcpy(_str, s._str);
//}
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
//现代写法 s1(s2)
string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);//构造
swap(tmp);//this->swap(s);
}
//赋值
// 传统写法
//string& operator=(const string& s)
//{
// if (this != &s)
// {
// char* tmp = new char[s._capacity];
// strcpy(tmp, s._str);
// delete[] _str;
// _str = tmp;//将tmp字符串直接给_str,类似浅拷贝
// _size = s._size;
// _capacity = s._capacity;
// }
// return *this;
//}
//现代写法
//string& operator=(const string& s)
//{
// if (this != &s)
// {
// string tmp(s);//拷贝构造
// swap(tmp);
// }
// return *this;
//}
// 极致写法,效率与上面一样
string& operator=(string tmp)//参数直接调用拷贝构造
{
swap(tmp);
return *this;
}
~string()
{
cout << "~string()" << endl;
delete[] _str;
_size = _capacity = 0;
}
private:
char* _str;
size_t _size;//有效存储字符个数
size_t _capacity;//实际空间
};
}