C++string类实现

每篇激励自己的一句话:路,是一直在脚下,好好努力,明日必定越来越好。

‍string类实现

string构造函数

在C++98语法中,string的构造函数就多达7种,其中(1)、(2)、(4)使用较为频繁。下面我们就模拟实现其构造函数。
《C++string类实现》
这里我们写的是一个缺省的构造函数,如果传进来的字符串长度<=15,那我们就将其容量设置为15;否则就是该字符串长度的两倍。

string(const char* str="")
{ 
	// 构造string类对象时,如果传递nullptr指针,认为程序非法,此处断言下
	if (str == nullptr)
	{ 
		assert(false);
	}
	this->m_size = strlen(str);//计算字符串的长度,不包括\0
	if (this->m_size <=15)	//如果该字符串长度<=15,则默认初始化其容量为15
	{ 
		this->m_capacity = 15;
	}
	else
	{ 
		this->m_capacity = strlen(str);
	}
	this->m_str = new char[this->m_capacity + 1];	//为存储字符串开辟空间(多开一个用于存放'\0')
	strcpy(this->m_str, str);	//将形参字符串拷贝给我们string对象的字符串数组中
}

拷贝构造函数

在模拟实现拷贝构造函数前,我们应该首先了解深浅拷贝:

  • 在默认的拷贝函数中(亦指浅拷贝),复制的策略是直接将原对象的数据成员值按照“位拷贝”的方式复制给新对象中对应的数据成员。但倘若类中含有指针变量,那么拷贝构造函数和赋值运算符重载函数就隐含了错误。
    • 以类 string 的两个对象 a,b 为例,假设 a.m_data的内容为”hello”, b.m_data 的内容为”world”。现将 a 赋给 b,赋值函数的“位拷贝”意味着执行 b.m_data = a.m_data。
      这将造成以下三个错误:
      ①是 b.m_data 原有的内存没被释放,造成内存泄露。
      ②是b.m_data 和 a.m_data 指向同一块内存,a 或 b任何一方变动都会影响另一方。
      ③是在对象被析构时,m_data 被释放了两次。
  • 对于深拷贝,针对成员变量存在指针的情况,不仅仅是简单的指针赋值,而是重新分配内存空间。

下面就是我们的拷贝构造的不同写法:
第一种写法
①首先我们在初始化列表中,将this指针对象的m_str的空间开辟好。
②接着我们把str.m_size 和str.m_capacity的值赋给this对象中的m_size 与m_capacity。
③最后我们把str.m_size里面的内容拷贝一份到this对象的m_str中。

string(const string& str) : m_str(new char[str.m_capacity + 1]), m_capacity(str.m_capacity), m_size(str.m_size)
{ 
	//在初始化列表中,我们已经将其容量和大小已经初始化好
	//在函数体内,我们只需要将string的对象中的str.m_str拷贝给我们this指针中的str.m_str
	strcpy(this->m_str, str.m_str);
}

第二种写法
原理:基于临时的局部对象初始化调用构造函数,然后出了函数就会调用析构函数释放其内存。
①先创建一个临时对象,让后用形参str中的m_str给临时对象让其调用构造函数。
②this对象在与临时对象交换其数据。

string(const string& str)
{ 
	string temp(str.m_str);	//调用构造函数,构造出一个字符串为str._str的对象
	this->swap(temp);//最后在让临时对象temp与this对象
}

上面的swap函数不是std命名空间的,是自己的命名空间中的一个string类的成员函数,在后面会给大家看到。

赋值运算符重载函数

在拷贝构造函数中我们已经说明赋值运算符重载函数在碰到有指针变量的类时也涉及深浅拷贝问题,我们同样需要采用深拷贝。下面也提供深拷贝的两种写法:
第一种写法
第一种写法和拷贝构造函数的第一种写法相差不大。唯一的区别在于如果这个对象本身自己给自己赋值时候需要特殊处理。

string& operator =(string& str)
{ 
	if (this != &str)	//防止自己给自己赋值
	{ 
		char *temp=new char[str.m_size+1];	//创建str字符个数+1的空间,因为要给‘\0’留个空间
		delete this->m_str;	//将原来this对象的m_str指向的空间释放
		strcpy(this->m_str,temp);
		this->m_size=str.m_size;
		this->m_capacity=str.m_capacity;
	}
	return *this;
}

第二种方法
相较于第一种写法,第二种写法代码就简单很多,但我们要了解其运行原理。这里面同样要防止自我赋值的情况。
原理:基于临时的局部对象用已有的对象给其初始化,然后调用拷贝构造函数,同样局部string对象出了函数就会调用析构函数释放其内存。
①首先创建一个临时变量,让其调用拷贝构造函数。
②在让this对象与临时对象temp交换。

string& operator =(string& str)
{ 
	if (this != &str)
	{ 
		string temp(str);
		swap(temp);
	}
	return *this;
}

析构函数

析构函数就没有什么好说的,但是还是要处理一些已经delete释放的空间,再一次delete释放,造成程序崩溃。

		~string()
		{ 
			if (this->m_str != nullptr)
			{ 
				delete[]this->m_str;	//销毁空间
				this->m_str = nullptr;	//并把该对象的指针置nullptr
				//这下面两行写不写都行
				this->m_capacity = 0;
				this->m_size = 0;
			}
		}

迭代器与npos

npos 是string类中的一个静态成员常量,它的值大小是 -1,但由于是size_t (unsigned int)类型,它的值在使用时会变成无符号的最大值。静态成员必须在类外初始化,类内声明。

public:
	typedef char* iterator;		//普通迭代器
	typedef const char* const_iterator;	//常量迭代器
	const static  size_t npos;	

注意:在string中的迭代器是用指针实现这并不代表着所有的迭代器都是用指针实现的(这很重要)

️begin与end

在string中有begin(起始迭代器)和end(结束迭代器),当然实际上在string不止有这两种迭代器,但这两个却是我们最经常使用的。begin()它指向的是第一的元素,end()指向的最后一个元素的下一个位置。如下图所示:
《C++string类实现》
下面我们就实现这两个迭代器函数。

iterator begin()	//起始迭代器
{ 
	//返回该对象的实例,也就是m_str这个char类型指针的首地址
	return this->m_str;
}

iterator end()		//结束迭代器
{ 
	//返回起始就是该字符串中'\0'的地址
	return this->m_str + this->m_size;
}

const_iterator begin() const	//具有常属性的起始迭代器
{ 
	return this->m_str;
}

const_iterator end() const 		//具有常属性的结束迭代器
{ 
	return this->m_str + this->m_size;
}

容量、大小与之相关函数

️capacity()、size()与empty()

capacity()与size()的含义分别是用来获取容量、大小。为什么会存在这两个接口呢?这是因为由于其不想让我们直接去访问它的私有成员,所以string类设置了size和capacity这两个成员函数。
empty是用来判断该对象是否为空。

size_t size() const		//这里必须设置成const
{ 
	//这里的个数是不包括'\0'的长度,与strlen相同
	return this->m_size;
}

size_t capacity() const	//这里必须设置成const
{ 
	//返回容量的大小
	return this->m_capacity;
}

bool empty() const
{ 
	return this->m_size==0;
}

️reserve

功能:reserve是用来预留空间大小
规则:
 1、当n大于对象当前的capacity时,将capacity扩大到n。
 2、当n小于等于对象当前的capacity时,什么也不做。

void reserve(size_t n = 0)	//给该函数设置了缺省值 0
{ 
	//如果n大于对象当前的capacity时,将capacity扩大到n
	if (n > this->m_capacity)
	{ 
		char* str = new char[n + 1];
		
		strncpy(str,this->m_str,this->m_size+1);
		delete[] this->m_str;
		this->m_str = str;
		this->m_capacity=n;
	}
}

上面为什么要用strncpy而不用strcpy呢?
这是因为里面存储的字符就是’\0‘,并且’\0‘也是我们想要的字符时,strcpy函数是从前向后拷贝,碰到’\0’就结束。而strncpy是指定拷贝多少个字节的大小,+1是因为我们字符串结束要用’\0‘。如下图所示:
《C++string类实现》

️resize

功能:将字符串大小调整为n个字符的长度。
规则:
1、如果n小于当前字符串长度,则当前值将缩短为其前n个字符,删除第n个字符以外的字符。
2、如果n大于当前字符串长度,则通过在末尾插入所需数量的字符来扩展当前内容,以达到n的大小。如果指定了字符c,新元素将被初始化为c的副本,否则,它们是值初始化字符’\0‘(空字符)。

void resize(size_t n, char c='\0')	//给一个默认的缺省值’\0‘
{ 
	if (n > this->m_size)	//如果n大于当前字符串长度
	{ 
		if (n>this->m_capacity)	//判断它是否要扩容
		{ 
			reserve(n);
		}
		
		int temp=n - this->m_size;
		while (temp--) 
		{ 
			push_back(c);
		}
	}
	else
	{ 
		this->m_size = n;
		this->m_str[this->m_size] = '\0';
	}
}

增删交换函数

️push_back()

功能:push_back()这个接口正如的它的名字一样,意思是在它字符串尾部插入一个字符。

《C++string类实现》

实现过程:
①首先插入,我们要判断其容量是否已满,如果已满则需要扩容后再插入,这里我们就可以简单的扩容为原来的两倍。
②插入数据,并让个数+1。
③再使this->m_size的位置的数据置为’\0‘,因为其这是字符串结束的标志。

void push_back(char c)	//尾插
{ 
	if (this->m_size == this->m_capacity)		//此时已满
	{ 
		reserve(this->m_capacity * 2);
	}
	this->m_str[this->m_size++] = c;
	this->m_str[this->m_size] = '\0';
}

️append()

append函数的重载版本有很多,这里我们只实现最常用的三个。
功能:与push_back效果类似,是在尾部插入字符或字符串。

string& append(const char ch)
{ 
	//在尾部插入一个字符ch,这里我们复用了push_back()函数
	this->push_back(ch);
	return *this;
}

//下面的两个都是在尾部插入一个字符串
//实现过程:
//①先判断加上字符串个数后我们的容量是否超出
//②再把str拷贝到this->m_str的尾部(也就是this->m_str + this->m_size)
string& append(const char* str)
{ 
	int len = this->m_size + strlen(str);
	if (len > this->m_capacity)
	{ 
		reserve(len * 2);
	}
	strcpy(this->m_str + this->m_size, str);
	this->m_size = len;
	return *this;
}
//与上面的思路相同
string& append(const string& str)
{ 
	int len= this->m_size + str.size();
	
	if (len > this->m_capacity)
	{ 
		reserve(len * 2);
	}
	strcpy(this->m_str + this->m_size, str.m_str);
	this->m_size=len;
	return *this;
}

️operator +=

由于operator+=函数与append的功能一模一样,但为什么我们不直接使用append()函数而多弄一个重载+=运算符,这是由于+=更通俗易懂,看到就大概知道怎么使用,上手成本低。

//这三个函数都是复用的push_back()或append函数,因为它们的功能是一模一样的
string& operator+=(const char ch)
{ 
	this->push_back(ch);
	return *this;
}

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

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

️insert()

功能:在pos指示的字符前面的字符串中插入其他字符。
《C++string类实现》

// 在pos位置上插入字符c/字符串str
//实现过程
//①首先先判断插入的位置是否合法
//②在判断插入n个字符后容量是否充足,不够则扩容
//③把pos位置包括pos及后面的字符向后移动len-pos个字节
//④从pos位置开始插入n个字符c
string& insert(size_t pos, size_t n,const char c)
{ 
	assert(pos <= this->size());	//判断插入的位置是否合法
	int len = this->m_size;			//存储插入前的字符个数
	this->m_size += n;				//加上n个字符后,判断其是否超出容量
	if (this->m_size > this->m_capacity)
	{ 
		reserve(this->m_size*2);	//扩容
	}
	//把pos位置包括pos及后面的字符向后移动len-pos个字节
	memmove(begin() + pos + n, begin() + pos, len-pos);
	//从pos位置开始到pos+n个字符,赋值为 c
	for (size_t i = pos; i < pos + n; ++i)
	{ 
		this->m_str[i] = c;
	}
	return *this;
}

/* 实现过程 ①首先先判断插入的位置是否合法 ②在判断插入这个字符串后容量是否充足,不够则扩容 ③把pos位置包括pos及后面的字符向后移动len-pos个字节 ④在把字符串里的内容挪动str.m_size个字节到我们的this对象的pos位置 */
string& insert(size_t pos, const string& str)
{ 
	assert(pos <= this->size());	//判断插入的位置合法性
	int len = this->m_size;//记录当前的长度
	this->m_size += str.m_size;
	if (this->m_size > this->m_capacity)//判断加上字符串后长度是否超过容量,超过就扩容
	{ 
		this->reserve(this->m_size * 2);
	}
	memmove(this->begin() + pos + str.m_size, this->begin() + pos, len - pos);
	memmove(this->begin() + pos, str.m_str, str.m_size);
	return *this;
}

string& insert(size_t pos, const char* str)
{ 
	assert(pos <= this->size());	//判断插入的位置合法性
	
	int len = this->m_size;	//记录当前的长度
	this->m_size += strlen(str);
	if (this->m_size > this->m_capacity)//判断加上字符串后长度是否超过容量,超过就扩容
	{ 
		this->reserve(this->m_size * 2);
	}
	memmove(this->begin() + pos + strlen(str), this->begin() + pos, len - pos);
	memmove(this->begin() + pos, str, strlen(str));
	return *this;
}

️pop_back()

功能:pop_back()这个接口正如的它的名字一样,意思是在它字符串尾部删除一个字符。
《C++string类实现》

void pop_back()		//尾删
{ 
	this->m_str[--this->m_size] = '\0';
}

️erase()

功能:erase函数的作用是删除字符串任意pos位置开始的n个字符。默认是从pos位置后面删除完。首先在删除之前我们也是要判断它删除的位置正确与否。
删除时有两种情况:
①当从pos位置开始后没有 len个字符或则没有传len的参数时,则表明后面全部删除。此时我们只需要将pos位置上值改为‘\0’做逻辑上的删除,并把个数改为pos个。
《C++string类实现》
②只删除pos位置开始及其之后的一部分字符。
《C++string类实现》

//删除从pos开始的n个字符
string& erase(size_t pos = 0, size_t len =string::npos)
{ 
	assert(pos <this->size());	//判断删除的位置是否正确
	if (pos + len >= this->m_size)	//pos位置开始后没有 len个字符或则没有传len的参数时
	{ 
		//做逻辑上的删除 
		this->m_size = pos;
		this->m_str[pos] = '\0';
	}
	else
	{ 
		for (size_t i = pos,j= pos + len; i<size()&&j<size(); ++i,++j)
		{ 
			this->m_str[i] = this->m_str[j];
		}
		this->m_size -= len;
		this->m_str[this->m_size] = '\0';
	}
	return *this;
}

️clear()

功能:清空字符串的内容,该字符串成为空字符串(长度为0个字符)。容量不一定会改变成0,因为我们可能会清空后继续使用。

void clear()		//清空
{ 
	this->m_size = 0;
	this->m_str[this->m_size] = '/0';
}

️swap()

功能:用于两个string之间进行交换

void swap(string& str)
{ 
	//这里我们调用命名空间std中的swap,将两个str的实例进行交换
	std::swap<size_t>(this->m_capacity, str.m_capacity);
	std::swap<size_t>(this->m_size, str.m_size);
	std::swap<char*>(this->m_str, str.m_str);
}

元素访问函数

️at()

at函数相当于数组的[]一样,传入一个下标的值进去,会给你返回该下标在数组中所对应的字符。

char& at(size_t pos)	//at(可读可写)
{ 
	assert(pos < this->m_size);//判断合法性
	return this->m_str[pos];
}

const char& at(size_t pos) const	//const对象调用
{ 
	assert(pos < this->m_size);//判断合法性
	return this->m_str[pos];
}

️operator []

operator []与at函数功能完全相同,只是[]看起来更简单易懂。[ ]运算符的重载是为了让string对象能像数组一样能通过[ ] +下标的方式获取字符串对应位置的字符。实现[ ] 运算符的重载时只需返回对象C字符串对应位置字符的引用即可,这样便能实现对该位置的字符进行读取和修改操作了,但需要注意在此之前检测所给的下标合法性。

char& operator[](size_t index)	//[]运算符重载(可读可写)
{ 
	assert(index < this->m_size);	//判断合法性
	return this->m_str[index];
}

在常对象调用operator[]时,我们只能用[ ] +下标的方式获取字符而不能对其进行修改。如果用上面的那个operator[]返回的是引用,说明是可读可写,但我们的对象是常对象,说明是不可以更改的,所以这是我们为什么多写一个功能差不多类似的函数。

const char& operator[](size_t index) const 	//const对象调用
{ 
	assert(index < this->m_size);	//判断合法性
	return this->m_str[index];
}

字符串操作函数

️find()

find函数是用于在字符串中查找一个字符或则是字符串,find函数用于正向查找,即从字符串开头开始向后查找。下面是它的两种重载版本。

// 返回c在string中第一次出现的位置
size_t find(const char c, size_t pos = 0) const
{ 
	for (size_t i = pos; i < this->size(); ++i)
	{ 
		if (this->m_str[i] == c)
		{ 
			return i;
		}
	}
	return npos;
}

// 返回子串s在string中第一次出现的位置
size_t find(const char* str, size_t pos = 0) const
{ 
	assert(pos < this->m_size);	首先先判断所给pos的合法性,
	int index = 0;
	size_t i = pos, j = 0;
	//我们暴力求解,一个个的去匹配,如果不相同就回溯,相同就继续
	while( i < size() && j<strlen(str))
	{ 
		if (this->m_str[i] == str[j])
		{ 
			++i, ++j;
		}
		else	//回溯
		{ 
			i = ++index;
			j = 0;
		}
	}
	
	return j==strlen(str)?index:string::npos;
}

️c_str()

这个接口是为了与C的一些代码结合产生的,其功能是:返回C类型的字符串。

const_iterator c_str() const
{ 
	return this->m_str;	//返回C类型的字符串
}

非成员函数重载

关系运算符有 >、>=、<、<=、==、!= 这6个,加上两个友元函数operator<<、operator>>,一个全局函数getline,一共9个。下面是它们的实现。

️operator<

bool operator< (const string &str) const	//<运算符重载
{ 
	
	if (strcmp(this->m_str, str.m_str) < 0)
	{ 
		return true;
	}
	return false;
}

️operator>

bool operator>(const string& str) const		//>运算符重载
{ 	
	if (strcmp(this->m_str, str.m_str)>0)
	{ 
		return true;
	}
	return false;
}

️operator==

bool operator==(const string& str) const	//==运算符重载
{ 
	if (strcmp(this->m_str, str.m_str) ==0)
	{ 
		return true;
	}
	return false;
}

️operator<=

bool operator<=(const string& str) const	//<=运算符重载
{ 
	if (!(*this > str))		//复用重载的>
	{ 
		return true;
	}
	return false;
}

️ operator>=

bool operator>=(const string& str) const
{ 
	if (!(*this < str))	//复用重载的<
	{ 
		return true;
	}
	return false;
}

️operator!=

bool operator!=(const string& str) const	//!=运算符重载
{ 
	if (!(*this == str))	//复用重载的==
	{ 
		return true;
	}
	return false;
}

️operator<<

功能:重载<<运算符是为了让string对象能够像内置类型一样使用<<运算符直接输出打印。

//因为我们要访问它的私有成员,故我将其重载为友元函数
ostream& operator<< (ostream& os, const string& str)
{ 
	//这里不能直接os<<str
	for (size_t i = 0; i < str.size(); ++i)
	{ 
		os << str.m_str[i];
	}
	return os;	//返回os,以便链式的输出。
}

️operator>>

重载>>运算符是为了让我们模拟的string对象能够像内置类型一样使用>>运算符直接输入。输入前我们需要先将对象里的C字符串清空,然后从标准输入流读取字符,直到读取到’ ‘(空格)或是’\n’便停止读取。

//因为我们要访问它的私有成员,故我将其重载为友元函数
istream& operator>> (istream& is, string& str)
{ 
	str.clear();	//如果str中已经有元素了,则把的内容清空

	char ch;
	ch = is.get();	
	while (ch==' '|| ch  == '\n')	//读取一个字符后判断它是不是有效字符,如果是’ ‘(空格)或是’\n’就继续读取
	{ 
		ch = is.get();
	}
	//到了这里就表示我们已经至少读到过一个不是’ ‘(空格)或是’\n的字符,此时我们直到读取到’ ‘(空格)或是’\n’便停止读取。
	while (ch != ' ' && ch != '\n')
	{ 
		str.push_back(ch);
		ch = is.get();
	}

	return is;	//返回is,以便链式的输出。
}

可能有的小伙伴会问operator>>与operator<<为什么不直接重载为成员函数?
简单来说就是this对象会和cout与cin抢第一个位置,即cout与cin的左边。

️getline

getline函数用于读取一行含有空格的字符串。实现时基于>>运算符的重载基本相同,只是当读取到’\n’的时候才停止读取字符。

istream& getline(istream& is, string& str)
{ 
	str.clear();	//如果str中已经有元素了,则把的内容清空
	char ch;
	ch = is.get();
	while (ch == '\n')	//读取一个字符后判断它是不是有效字符,如果是’\n’就继续读取
	{ 
		ch = is.get();
	}
	//到了这里就表明至少读取到一个不是’\n’的字符,此时我们如果读取到’\n’便停止读取。
	while (ch != '\n')	
	{ 
		str.push_back(ch);
		ch = is.get();

	}
	return is;	//返回is,以便链式的输出。
}

完整代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<string.h>
#include<assert.h>
#include<iostream>
using namespace std;
namespace  ZJ
{ 
	class string
	{ 
		friend ostream& operator<< (ostream& os, const string& str);
		friend istream& operator>> (istream& is, string& str);
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
		const static  size_t npos;
	public:
		string(const char* str="")
		{ 
			// 构造string类对象时,如果传递nullptr指针,认为程序非法,此处断言下
			if (str == nullptr)
			{ 
				assert(false);
			}
			this->m_size = strlen(str);//计算字符串的长度,不包括\0
			if (this->m_size <=15)	//如果该字符串长度<=15,则默认初始化其容量为15
			{ 
				this->m_capacity = 15;
			}
			else
			{ 
				this->m_capacity = strlen(str);
			}
			this->m_str = new char[this->m_capacity + 1];	//为存储字符串开辟空间(多开一个用于存放'\0')
			strcpy(this->m_str, str);	//将形参字符串拷贝给我们string对象的字符串数组中
		}

		string(const string& str) : m_str(new char[str.m_capacity + 1]), m_capacity(str.m_capacity), m_size(str.m_size)
		{ 
			strcpy(this->m_str, str.m_str);
		}

		void swap(string& str)
		{ 
			std::swap<size_t>(this->m_capacity, str.m_capacity);
			std::swap<size_t>(this->m_size, str.m_size);
			std::swap<char*>(this->m_str, str.m_str);
		}
		string& operator =(string& str)
		{ 
			if (this != &str)
			{ 
				string temp(str);
				swap(temp);
			}
			return *this;
		}

		~string()
		{ 
			if (this->m_str != nullptr)
			{ 
				delete[]this->m_str;
				this->m_capacity = 0;
				this->m_size = 0;
				this->m_str = nullptr;
			}
		}

		const_iterator c_str() const
		{ 
			return this->m_str;
		}

		size_t size() const
		{ 
			return this->m_size;
		}

		size_t capacity() const
		{ 
			return this->m_capacity;
		}

		bool empty() const
		{ 
			return this->m_size==0;
		}

		char& operator[](size_t index)
		{ 
			assert(index < this->m_size);
			return this->m_str[index];
		}

		const char& operator[](size_t index) const 
		{ 
			assert(index < this->m_size);
			return this->m_str[index];
		}

		char& at(size_t pos)
		{ 
			assert(pos < this->m_size);
			return this->m_str[pos];
		}

		const char& at(size_t pos) const
		{ 
			assert(pos < this->m_size);
			return this->m_str[pos];
		}

		//删除从pos开始的n个字符
		string& erase(size_t pos = 0, size_t len =string::npos)
		{ 
			assert(pos <this->size());
			if (pos + len >= this->m_size)
			{ 
				this->m_size = pos;
				this->m_str[pos] = '\0';
			}
			else
			{ 
				for (size_t i = pos,j= pos + len; i<size()&&j<size(); ++i,++j)
				{ 
					this->m_str[i] = this->m_str[j];
				}
				this->m_size -= len;
				this->m_str[this->m_size] = '\0';
			}
			return *this;
		}

		// 在pos位置上插入字符c/字符串str
		string& insert(size_t pos, size_t n,const char c)
		{ 
			assert(pos <= this->size());
			int len = this->m_size;
			this->m_size += n;
			if (this->m_size > this->m_capacity)
			{ 
				reserve(this->m_size*2);
			}

			memmove(begin() + pos + n, begin() + pos, len-pos);
			for (size_t i = pos; i < pos + n; ++i)
			{ 
				this->m_str[i] = c;
			}
			return *this;
		}

		string& insert(size_t pos, const string& str)
		{ 
			assert(pos <= this->size());
			string temp = *this;
			int len = temp.m_size;
			temp.m_size += str.m_size;
			if (temp.m_size > this->m_capacity)
			{ 
				temp.reserve(this->m_size * 2);
			}
			memmove(temp.begin() + pos + str.m_size, temp.begin() + pos, len - pos);
			memmove(temp.begin() + pos, str.m_str, str.m_size);
			swap(temp);
			return *this;
		}

		string& insert(size_t pos, const char* str)
		{ 
			assert(pos <= this->size());
			string temp = *this;
			int len = this->m_size;
			temp.m_size += strlen(str);
			if (temp.m_size > this->m_capacity)
			{ 
				temp.reserve(temp.m_size * 2);
			}
			memmove(temp.begin() + pos + strlen(str), temp.begin() + pos, len - pos);
			memmove(temp.begin() + pos, str, strlen(str));
			swap(temp);
			return *this;
		}

		void reserve(size_t n = 0)
		{ 
			if (n > this->m_capacity)
			{ 
				char* str = new char[n + 1];
				strncpy(str,this->m_str,this->m_size+1);
				delete[] this->m_str;
				this->m_str = str;
				this->m_capacity=n;
			}
		}

		void resize(size_t n, char c='\0')
		{ 
			if (n > this->m_size)
			{ 
				if (n>this->m_capacity)
				{ 
					reserve(n);
				}
				while (n - this->m_size)
				{ 
					push_back(c);
				}
			}
			else
			{ 
				this->m_size = n;
				this->m_str[this->m_size] = '\0';
			}
		}
		string& append(const char ch)
		{ 
			this->push_back(ch);
			return *this;
		}

		string& append(const char* str)
		{ 
			int len = this->m_size + strlen(str);
			if (len > this->m_capacity)
			{ 
				reserve(len * 2);
			}
			strcpy(this->m_str + this->m_size, str);
			this->m_size = len;
			return *this;
		}

		string& append(const string& str)
		{ 
			int len= this->m_size + str.size();
			
			if (len > this->m_capacity)
			{ 
				reserve(len * 2);
			}
			strcpy(this->m_str + this->m_size, str.m_str);
			this->m_size=len;
			return *this;
		}

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

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

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

		iterator begin()	//起始迭代器
		{ 
			return this->m_str;
		}

		iterator end()		//结束迭代器
		{ 
			return this->m_str + this->m_size;
		}
		const_iterator begin() const
		{ 
			return this->m_str;
		}

		const_iterator end() const 		//结束迭代器
		{ 
			return this->m_str + this->m_size;
		}

		char& back()		//获得最后一个元素
		{ 
			return this->m_str[this->m_size - 1];
		}

		char& front()	//获得第一个元素
		{ 
			return this->m_str[0];
		}

		void push_back(char c)	//尾插
		{ 
			if (this->m_size == this->m_capacity)		//此时已满
			{ 
				reserve(this->m_capacity * 2);
			}
			this->m_str[this->m_size++] = c;
			this->m_str[this->m_size] = '\0';
		}

		void pop_back()		//尾删
		{ 
			this->m_str[--this->m_size] = '\0';
		}

		void clear()		//清空
		{ 
			this->m_size = 0;
			this->m_str[this->m_size] = '/0';
		}

		bool operator< (const string &str) const
		{ 
			if (strcmp(this->m_str, str.m_str) < 0)
			{ 
				return true;
			}
			return false;
		}
		
		bool operator==(const string& str) const
		{ 
			if (strcmp(this->m_str, str.m_str) ==0)
			{ 
				return true;
			}
			return false;
		}

		bool operator>(const string& str) const
		{ 
			if (strcmp(this->m_str, str.m_str)>0)
			{ 
				return true;
			}
			return false;
		}


		bool operator<=(const string& str) const
		{ 
			if (!(*this > str))
			{ 
				return true;
			}
			return false;
		}


		bool operator>=(const string& str) const
		{ 
			if (!(*this < str))
			{ 
				return true;
			}
			return false;
		}

		bool operator!=(const string& str) const
		{ 
			if (!(*this == str))
			{ 
				return true;
			}
			return false;
		}

		// 返回c在string中第一次出现的位置
		size_t find(const char c, size_t pos = 0) const
		{ 
			for (size_t i = pos; i < this->size(); ++i)
			{ 
				if (this->m_str[i] == c)
				{ 
					return i;
				}
			}
			return npos;
		}

		// 返回子串s在string中第一次出现的位置
		size_t find(const char* str, size_t pos = 0) const
		{ 
			assert(pos < this->m_size);
			int index = 0;
			size_t i = pos, j = 0;
			while( i < size() && j<strlen(str))
			{ 
				if (this->m_str[i] == str[j])
				{ 
					++i, ++j;
				}
				else
				{ 
					i = ++index;
					j = 0;
				}
			}
			
			return j==strlen(str)?index:string::npos;
		}
	private:
		char* m_str;			
		size_t m_size;		//个数
		size_t m_capacity;	//容量
	};
	const size_t string::npos = -1;

	ostream& operator<< (ostream& os, const string& str)
	{ 
		for (size_t i = 0; i < str.size(); ++i)
		{ 
			os << str.m_str[i];
		}
		return os;
	}

	istream& operator>> (istream& is, string& str)
	{ 
		str.clear();

		char ch;
		ch = is.get();
		while (ch==' '|| ch  == '\n')
		{ 
			ch = is.get();
		}
		while (ch != ' ' && ch != '\n')
		{ 
			str.push_back(ch);
			ch = is.get();
		}

		return is;
	}

	istream& getline(istream& is, string& str)
	{ 
		str.clear();
		char ch;
		ch = is.get();
		while (ch == '\n')
		{ 
			ch = is.get();
		}
		while (ch != '\n')
		{ 
			str.push_back(ch);
			ch = is.get();
		}
		return is;
	}
}

《C++string类实现》
如有错误之处,还请各位指出,谢谢大家!!!
END...

点赞