STL容器——string


前言: 标准库中的string类很好用,相对C语言的字符操作函数,刷题的时候我们也常用string类,本文先介绍如何使用string类,后面会有string类的模拟实现,帮助大家更深入的理解string。


1.string类的常用接口

下面介绍string类最常用的接口,掌握以下基本就可以使用string类了,详细信息可以自己查询这个网站,string类接口详解,里面很全的,不过是全英文的。

1.1 string类的构造函数

在这里插入图片描述
我们依次实现一下:

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s;
	string s1("hellow");
	string s2(10,'x');
	string s3(s1);
	return 0;
}

通过调试去查看对象
在这里插入图片描述

1.2 string类的容量操作

在这里插入图片描述
size和length完全一样,为了和其它容器的接口保持一致,所以引入的size。
例子:
1.输出字符串的长度以及空间大小

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s("hollow world");
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	return 0;
}

运行结果:
在这里插入图片描述
capacity是它的容量,size/length是它有效字符的长度。
2. 判断上面的string类对象s是否为空?如果不为空就清空字符串,清空后再输出有效字符。

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s("hollow world");
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	if (!s.empty())
		s.clear();
	cout << s.size() << endl;	
	return 0;
}

运行结果:
在这里插入图片描述
有效字符被清空,所以size为0,那容量会被情况嘛?不会,这个大家去验证,容量是不变的。
3.创建一个字符串s1,它的容量是100(capacity);再创建一个字符串s2它的有效字符数量是100(size)。

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1;
	s1.reserve(100);
	string s2;
	s2.resize(100);
	return 0;
}

通过调试来查看:
在这里插入图片描述
在这里插入图片描述
通过对比发现,reserve是调整容量;resize是调整有效字符的长度,而且默认初始为‘0’,也可以不初始为0,可以自己定义,比如:

s2.resize(100,'x');

这样就会被初始成‘x’。
注意

  • reserve:给字符串预留空间,只能是空间变大,如果原来的字符串capacity为10,你用reserve调成1,是调不成功的。
  • resize:调整有效字符长度,这个是随意调整的,调大也行,调小也可以,如果调小的话,就会把超出的字符丢掉; 调大有效字符是在尾部追加,默认是追加‘0’,当然这个可以自己定义。
1.3 string类的访问/遍历

在这里插入图片描述
string是支持随机访问的,所以可以用[]来访问;也可以使用迭代器;范围for(语法糖)也是支持的,它的底层也是封装的迭代器。
所以综上有三种方式来访问string类的对象;
我们来一 一实现:

  1. operator[]
#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s("hellow world");
	for (size_t i = 0; i < s.size(); i++)
	{
		cout << s[i];
	}
	return 0;
}
  1. 迭代器begin+end /rbegin+rend
#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s("hellow world");
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it;
		++it;
	}
	cout<<endl;
	string::reverse_iterator it1 = s.rbegin();
	while (it1 != s.rend())
	{
		cout << *it1;
		++it1;
	}
	
	return 0;
}

关于迭代器,这里先简单说,后面会有迭代器的实现;迭代器可以看作是返回指针的函数,begin返回的是s[0]的指针(第一个元素),end返回最后一个元素的指针;rbegin和rend则是和begin和end反着来的。所有的容器都支持迭代器,string我建议用[]下标的方式。
string的迭代器有:
在这里插入图片描述
begin和end
在这里插入图片描述

在这里插入图片描述
可以发现有两个重载,也就是有一个const版本,还有普通版本:

string::const_iterator it=s.begin();
string::const_iterator it=s.end();

rebgin和rend也都有两个版本:
在这里插入图片描述
在这里插入图片描述
就是这样,基本就可以使用迭代器了,用法比较简单,
看一下上面程序的运行结果:
一个正序打印,一个反序打印。
在这里插入图片描述

  1. 范围for(语法糖)
    之所以又叫语法糖,就是因为它用起来比较甜(简单);
    我们来用范围for去遍历一个字符串:
#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s("hellow world");
	for (auto ch : s)
	{
		cout << ch;
	}
	return 0;
}

auto 类型的ch会自动的拷贝字符串s的每一个值,如果想用范围for去修改字符串只需要在ch前加一个&,引用即可。

for (auto &ch : s)
	{
		ch+=1;
	}
1.4 string类对象的修改操作

在这里插入图片描述
在这里插入图片描述
以上的函数,可以修改字符串的内容,比如往里面插入一个字符:头插,尾插,中间插;将string类对象转换为c语言字符串形式;查找某个字符;只截取字符串中的一部分;删除某个字符;

  1. 插入操作
    push_back,append,+=,insert都能完成插入操作,我们都实现来看看
    (1)头插:这个只能用insert了
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s;
	s.insert(0, 1,'i');
	return 0;
}

(2)尾插:push_back,append,+=,insert都可以完成操作;

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s;
	s.insert(0, 1,'i');
	
	s.push_back('y');
	s.append(1,'o');
	s += 'u';
	return 0;
}

push_back支持尾插一个字符,+=,append,insert支持插入n个字符(多个重载,下去可以看看);
(3)中间插入:只能用insert,在’y’前插入字符串"love";

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s;
	s.insert(0, 1,'i');

	s.push_back('y');
	s.append(1,'o');
	s += 'u';

	s.insert(1, "love");
	return 0;
}

调试看一下,结果:
在这里插入图片描述
2. 返回c格式的字符串
明明都是字符串有啥可转换的?这就错了,c++中的那个是string类的对象,不是字符串;比如下面的操作,我们来看看会出现什么问题:

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s("hellow");
	const char* ps = s;
	return 0;
}

在这里插入图片描述
所以就到了c_str的出场了,

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s("hellow");
	const char* ps = s.c_str();
	cout << ps;
	return 0;
}

运行也没毛病:
在这里插入图片描述

  1. 查找
  • find有以下的重载:
    在这里插入图片描述
    pos代表从哪个位置开始从前往后找,默认是0;
  • rfind的重载和find一样,不过是从后往前找:
    在这里插入图片描述
    find/rifind找到了会返回 找到的具体位置也就是下标;如果找不到,就会返回一个巨大的值基本上咱们的string类对象在那么大的位置上没有数据,就是string::npos。
  1. 删除
    erase有以下的重载:
    在这里插入图片描述
erase(开始删除的位置,删除多少(默认是删除完));
  1. 手动向string输入
    如果像C语言中scanf一样,向某个字符串输入呢?
    string重载了运算符<<,>>;也就是说我们可以用cin,向string类对象输入,输出也可以用cout:
string sl;
cin>>sl;
cout<<sl;

但是需要注意一点,我们输入的时候,如果想要往string类对象中输入一个’ ‘空格,单纯用cin是不可以的,这可能会导致你刷题时出错,因为我们用cin输入的时候,输入’ ‘或者’\n’相当于结束本次输入;

解决方案:用getline就可以,它可以接受空格。

string sl;
getline(cin,sl);

2. string类的模拟实现

2.1 string类各个接口的实现
1. string类的private成员
class string
{
private:
char *_str;//指向存字符串的指针
size_t _size;//有效字符长度
size_t capicity;//容量
static const size_t nops;//静态成员nops,size_t无符号数,所以最大是-1(二进制全1)
}
const size_t string::nops = -1;//在类外初始化静态成员变量
2. 构造函数
     string(const char* str="")
			:_size(strlen(str))
			, _capicity(strlen(str))
		{
		     if(str==nullptr)
		     assert(false);
		     
			_str = new char[_size + 1];
			strcpy(_str, str);
		}

构造函数,默认传参为"",注意这个字符串里面有\0,不要以为里面是空的,有效字符为空,但是还有\0;如果传来空指针,直接断言报错;_str指向一个和传来字符一样大的空间,再把内容拷贝进去。
调试验证:
在这里插入图片描述

3. 析构函数
        ~string()
		{
			if(_str)//判断_str是否为空
			delete[] _str;
			_size = _capicity = 0;
		}
4. 拷贝构造
void Swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capicity, s._capicity);
		}
     //自己动手写  
        string(const string& str)
			:_size(str._size),
			 _capicity(str._capicity)
		{
			if (_str)
				delete[] _str;
			_str = new char[str._size + 1];
			strcpy(_str, str._str);
		}
	//复用构造函数
	string(const string& s)
			:_str(nullptr),
			_size(0),
			_capicity(0)
		{
			string tmp(s._str);
			Swap(tmp);
		}

这个拷贝构造,必须得写;默认得拷贝构造是浅拷贝,浅拷贝:按字节序拷贝。浅拷贝会导致_str和str._str指向同一个空间,在析构时会报错,因为一块空间不可以被析构多次。所以必须用深拷贝,深拷贝:给每个对象独立的空间,拷贝内容,不会共用一块空间。
在这里插入图片描述
在这里插入图片描述
可以看到上面所写的就是深拷贝,我先释放了原来所指的空间,再让它开辟一个和要拷贝对象中字符串一样的大小,最后拷贝内容就行了。

第二个版本是复用了构造函数,这是简单巧妙的写法。甚至不需要我们去手动的释放空间。我们构造了一个临时对象tmp,tmp的内容就是拷贝的传参来的_str。然后交换一下tmp和this->_str的内容就可以了。我们之前不是要手动的释放this->_str空间嘛,由于tmp交换了空间,而且它是临时对象,所以这个拷贝构造函数结束调用时,临时对象会自动的调用它的析构函数,也就释放了它的空间。

5. 重载运算符=

默认的=重载也是浅拷贝,所以需要自己实现。

        //传统版本
        string& operator= (const string& str)
		{
			if (this != &str)
			{
				char* tmp = new char[str._size+ 1];
				strcpy(tmp, str._str);
				_str = tmp;
				_size = str._size;
				_capicity = str._capicity;
			}
			return *this;
		}
		//现代版本
		string& operator=(string s)
		{
			Swap(s);
			return *this;
		}

传统版本:我创建了一个tmp,让它去开辟空间,并且拷贝内容,最后使.str=tmp,就可以了。当然上面的拷贝构造也可以用这样的方法。
现代版本:利用传值传参,会调用拷贝构造,也就是说s是传参的一份临时拷贝。然后只需要交换一下this和s,就可以,而且交换后,因为是临时拷贝,函数调用结束后就会自动的析构其空间。

6. 支持随机访问

随机访问上面也说过,就是三种方式:[],迭代器,范围for。我们都来实现一下:
(1)[]下标随机访问

       char& operator[](const size_t pos)
		{
			assert(pos < this->_size);
			return this->_str[pos];
		}

(2)迭代器
在string里迭代器就是指针,但是不是所有的迭代器都是指针,比如list

        typedef char*  iterator;
        typedef char* const_iterator;
		iterator begin()
		{ 
			return _str;
		}
		iterator end()
		{ 
			return _str + _size; 
		}
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}

(3)范围for就是利用的迭代器,上面写好迭代器后就可以使用,但是必须规范迭代器的函数比如:必须是begin,end这样的函数,如果写成Begin,End就不可以了,auto推不出来。

7. 容量操作

(1)reserve,调整容量;如果n>_capicity,那么需要扩大容量,我可以创建一个临时变量tmp,让它去开辟空间,并且把_str的字符拷贝到tmp中,然后把_str释放掉,最后用_str=tmp,_capicity=n,就完成了扩容。

void reserve(size_t n)
		{
			if (n > _capicity)
			{
				char* tmp = new char[n+1];
				strcpy(tmp,_str);

				delete[] _str;
				_str = tmp;
				_capicity = n;
			}
		}

(2)resize,调整有效字符的大小,这个分三种情况。具体实现如下:

        void resize(size_t n, char c = '\0')
		{
		    //有效字符变短 
			if (n <= _size)
			{
				_size = n;
				_str[_size] = '\0';
			}
			//有效字符变长
			else
			{
			    //容量不够容纳,扩容
				if (n > _capicity)
				{
					reserve(n + 1);
				}
				//将_str+_size后,n-_size长度都初始化成 c,之前的不动
				memset(_str+_size, c, n -_size);
				_size = n;
				//末尾必须有‘\0’
				_str[_size] = '\0';
			}
		}

(3)判断_str是否为空,查看容量,有效字符长度

        //有效字符长度
        size_t size()const
		{
			return _size;
		}
        //容量大小
		size_t capacity()const
		{
			return _capicity;
		}
        // 判断是否为空
		bool empty()const
		{
			if (_size == 0)
				return true;
			else
				return false;
		}
8.插入操作

(1)push_back,尾部插入一个字符,先要判断是否需要扩容

          void push_back(char c)
		  {
			if (_size == _capicity)
			{
				reserve(_capicity == 0 ? 4 : 2 * _size);
			}
			_str[_size] = c;
			_size++;
			_str[_size] = '\0';
		  }

(2)append,尾部插入一个字符串,判断扩容,copy一下就ok了。

        void append(const char* str)
		{
			int n = strlen(str);
			if (_size + n > _capicity)
			{
				reserve(_size + n+1);
			}
			strcpy(_str + _size, str);
			_size += n;
		}

(3)运算符重载+=,这个就简单了,直接复用上面的push_back,append.

        string& operator+=(char c)
		{
			this->push_back(c);
			return *this;
		}
        string& operator+=(const char* str)
		{
			this->append(str);
			return *this;
		}

(4)insert,插入字符,插入字符串都需实现

	    string& insert(char c, size_t pos=0)
		{
			assert(pos <= _size);
			//扩容
			if (_size == _capicity)
			{
				reserve(_capicity == 0 ? 4 : 2 * _size);
			}
			//挪动数据
			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				end--;
			}
			_str[pos] = c;
			++_size;
			return *this;
		}

		string& insert(const char* str,size_t pos=0)
		{
			assert(pos <= _size);
			int n = strlen(str);
			if (_size+n > _capicity)
			{
				reserve(_size+n+1);
			}
			size_t end = _size + n;
			while (end>pos+n)
			{
				_str[end] = _str[end - n];
				end--;
			}
			
		    strncpy(_str + pos, str, n);
		
			return *this;
		}
9.查找字符

查找一个字符比较简单,从头找,找到后返回下标,找不到返回nops。
查找一个字符串的话,可以自己实现,也能用strstr来实现。


		// 返回c在string中第一次出现的位置

		size_t find(char c, size_t pos = 0) const
		{
			for (size_t i = pos; i < this->_size; i++)
			{
				if (_str[i] == c)
					return i;
			}
			return nops;	
		}

		// 返回子串s在string中第一次出现的位置

		size_t find(const char* s, size_t pos = 0) const
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return nops;
			}
			else
			{
				return ptr-_str;
			}
		}
10.删除操作

(1)erase:
pos指的是删除某个位置,len代表删除多长的长度

        string& erase(size_t pos=0, size_t len=nops)
		{
			assert(pos < _size);
			if (len == nops || pos + len >= _size)
			{
			     //pos后面的位置全删,直接pos位置放'\0'就好了
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
			    //从后往前覆盖
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

(2)clear,全部删除

        void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
11.转成c形式的字符串
        const char* c_str()const
		{
			return _str;
		}
12.string对象间的比大小

比大小的话,只需要实现俩个(<,= =) 或者 ( >,= =),然后复用就可以了。比如:我实现了<== ,那么>就是<=取反,>=就是<取反。
我们实现直接利用strcmp函数就可以,
根据其返回值如下:

返回值大于0,就是是s1>s2成立
返回值小于0,就是是s1<s2成立
返回值等于0,就是是s1==s2成立
在这里插入图片描述

//实现==
        bool operator==(const string& s)
		{
			return strcmp(this->c_str(), s.c_str())==0;
		}
//实现<,不想用strcmp也能实现,感兴趣的可以看看		
		bool operator<(const string& s)
		{
			/*size_t s1 = 0; size_t s2 = 0;
			while (s1 < this->size() && s2 < s.size())
			{
				if ((*this)[s1] < s[s2])
				{
					return true;
				}
				else if((*this)[s1]>s[s2])
				{
					return false;
				}
				else
				{
					s1++;
					s2++;
				}
			}
			return  s2 < s.size() ? true : false;*/

			return strcmp(this->c_str(),s.c_str())<0;
		}

接下来复用即可。

        bool operator<=(const string& s)
		{
			return *this < s || *this == s;
		}

		bool operator>(const string& s)
		{
			return !(*this <= s);
		}

		bool operator>=(const string& s)
		{
			return !(*this < s);
		}
		
		bool operator!=(const string& s)
		{
			return !(*this == s);
		}
13. 重载<<>>

想要直接用cout输出string,用cin直接向string输入;那就需要重载<<和>>。

//直接定义在类里,友元函数,
//还得用std里的cin和cout
       friend std::ostream& operator<<(std::ostream& _cout, const bit::string& s)
		{
			//自己写
			/*for (size_t i = 0; i < s.size(); i++)
			{
				_cout << s[i];
			}*/
			//用auto省事
			for (auto ch : s)
			{
				_cout << ch;
			}
			return _cout;
		}

		friend std::istream& operator>>(std::istream& _cin, bit::string& s)
		{
			s.clear();
			char ch=_cin.get();
			while (ch != ' '&& ch != '\n')
			{
				s += ch;
				ch = _cin.get();
			}
			return _cin;
		}
2.2 模拟实现string的代码
namespace ly

{

	class string

	{

		friend std::ostream& operator<<(std::ostream& _cout, const ly::string& s)
		{
			//自己写
			/*for (size_t i = 0; i < s.size(); i++)
			{
				_cout << s[i];
			}*/
			//用auto省事
			for (auto ch : s)
			{
				_cout << ch;
			}
			return _cout;
		}

		friend std::istream& operator>>(std::istream& _cin, ly::string& s)
		{
			s.clear();
			char ch=_cin.get();
			while (ch != ' '&& ch != '\n')
			{
				s += ch;
				ch = _cin.get();
			}
			return _cin;
		}

	public:

		typedef char* iterator;
		typedef char* const_iterator;

	public:
		void Swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capicity, s._capicity);
		}

		string(const char* str = "")
			:_size (strlen(str)),
			_capicity(strlen(str))
		{
			_str = new char[_capicity+1];
			strcpy(_str, str);
		}

		string(const string& s)
			:_str(nullptr),
			_size(0),
			_capicity(0)
		{
			string tmp(s._str);
			Swap(tmp);
		}

		string& operator=(string s)
		{
			Swap(s);
			return *this;
		}
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capicity = 0;
		}
	//

	// iterator

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}
	/

    // modify

		void push_back(char c)
		{
			if (_size == _capicity)
			{
				reserve(_capicity == 0 ? 4 : 2 * _size);
			}
			_str[_size] = c;
			_size++;
			_str[_size] = '\0';
		}

		string& operator+=(char c)
		{
			this->push_back(c);
			return *this;
		}

		void append(const char* str)
		{
			int n = strlen(str);
			if (_size + n > _capicity)
			{
				reserve(_size + n+1);
			}
			strcpy(_str + _size, str);
			_size += n;
		}

		string& operator+=(const char* str)
		{
			this->append(str);
			return *this;
		}

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

		const char* c_str()const
		{
			return _str;
		}
		/
		// capacity

		size_t size()const
		{
			return _size;
		}

		size_t capacity()const
		{
			return _capicity;
		}

		bool empty()const
		{
			if (_size == 0)
				return true;
			else
				return false;
		}

		void resize(size_t n, char c = '\0')
		{
			if (n <= _size)
			{
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				if (n > _capicity)
				{
					reserve(n + 1);
				}
				memset(_str+_size, c, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
		}

		void reserve(size_t n)
		{
			if (n > _capicity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capicity = n;
			}
		}

		/

		// access

		char& operator[](size_t index)
		{
			return _str[index];
		}

		const char& operator[](size_t index)const
		{
			return _str[index];
		}

		/

		//relational operators

		bool operator<(const string& s)
		{
			/*size_t s1 = 0; size_t s2 = 0;
			while (s1 < this->size() && s2 < s.size())
			{
				if ((*this)[s1] < s[s2])
				{
					return true;
				}
				else if((*this)[s1]>s[s2])
				{
					return false;
				}
				else
				{
					s1++;
					s2++;
				}
			}
			return  s2 < s.size() ? true : false;*/

			return strcmp(this->c_str(),s.c_str())<0;
		}

		bool operator<=(const string& s)
		{
			return *this < s || *this == s;
		}

		bool operator>(const string& s)
		{
			return !(*this <= s);
		}

		bool operator>=(const string& s)
		{
			return !(*this < s);
		}

		bool operator==(const string& s)
		{
			return strcmp(this->c_str(), s.c_str())==0;
		}

		bool operator!=(const string& s)
		{
			return !(*this == s);
		}



		// 返回c在string中第一次出现的位置

		size_t find(char c, size_t pos = 0) const
		{
			for (size_t i = pos; i < this->_size; i++)
			{
				if (_str[i] == c)
					return i;
			}
			return nops;	
		}

		// 返回子串s在string中第一次出现的位置

		size_t find(const char* s, size_t pos = 0) const
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)
			{
				return nops;
			}
			else
			{
				return ptr-_str;
			}
		}

		// 在pos位置上插入字符c/字符串str,并返回该字符的位置

		string& insert(char c, size_t pos)
		{
			assert(pos <= _size);
			if (_size == _capicity)
			{
				reserve(_capicity == 0 ? 4 : 2 * _size);
			}
			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				end--;
			}
			_str[pos] = c;
			++_size;
			return *this;
		}

		string& insert(const char* str,size_t pos)
		{
			assert(pos <= _size);
			int n = strlen(str);
			if (_size+n > _capicity)
			{
				reserve(_size+n+1);
			}
			size_t end = _size + n;
			while (end>pos+n)
			{
				_str[end] = _str[end - n];
				end--;
			}
			strncpy(_str + pos, str, n);
			return *this;

		}
		// 删除pos位置上的元素,并返回该元素的下一个位置

		string& erase(size_t pos=0, size_t len=nops)
		{
			assert(pos < _size);
			if (len == nops || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}

	private:

		char* _str;
		size_t _capicity;
		size_t _size;

		static const size_t nops;
	};
	const size_t string::nops = -1;

};

3.面试时手撕string

面试当中写string,不需要完成太多的功能。string中只要有一个char *就可以了,不需要size,capicity,只需保证我们构造的string末尾有\0。而且考虑实现操作:构造、析构、拷贝构造、赋值。这四个操作就满足了。

#include <utility>
#include <string.h>

class String
{
 public:
  String()
    : data_(new char[1])
  {
    *data_ = '\0';
  }

  String(const char* str)
    : data_(new char[strlen(str) + 1])
  {
    strcpy(data_, str);
  }

  String(const String& rhs)
    : data_(new char[rhs.size() + 1])
  {
    strcpy(data_, rhs.c_str());
  }
  /* Delegate constructor in C++11
  String(const String& rhs)
    : String(rhs.data_)
  {
  }
  */

  ~String()
  {
    delete[] data_;
  }

  /* Traditional:
  String& operator=(const String& rhs)
  {
    String tmp(rhs);
    swap(tmp);
    return *this;
  }
  */
  String& operator=(String rhs) // yes, pass-by-value
  {
    swap(rhs);
    return *this;
  }

  // C++ 11
  String(String&& rhs)
    : data_(rhs.data_)
  {
    rhs.data_ = nullptr;
  }

  String& operator=(String&& rhs)
  {
    swap(rhs);
    return *this;
  }

  // Accessors

  size_t size() const
  {
    return strlen(data_);
  }

  const char* c_str() const
  {
    return data_;
  }

  void swap(String& rhs)
  {
    std::swap(data_, rhs.data_);
  }

 private:
  char* data_;
};

结尾语: 以上就是本篇string的内容,祝大家早日拿offer。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

动名词

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值