精通C++ STL(二):string类的模拟实现

目录

string类各函数接口总览

默认成员函数

  构造函数

  拷贝构造函数

  赋值运算符重载函数

  析构函数

迭代器相关函数

  begin和end

容量和大小相关函数

  size和capacity

  reserve和resize

  empty

修改字符串相关函数

  push_back

  append

  operator+=

  insert

  erase

  clear

  swap

  c_str

访问字符串相关函数

  operator[ ]

  find和rfind

  find函数

  rfind函数

关系运算符重载函数

>>和<<运算符的重载以及getline函数

  >>运算符的重载

  <<运算符的重载 

    getline


string类各函数接口总览

namespace russ
{
	//模拟实现string类
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		//默认成员函数
		string(const char* str = "");         //构造函数
		string(const string& s);              //拷贝构造函数
		string& operator=(const string& s);   //赋值运算符重载函数
		~string();                            //析构函数

		//迭代器相关函数
		iterator begin();
		iterator end();
		const_iterator begin()const;
		const_iterator end()const;

		//容量和大小相关函数
		size_t size();
		size_t capacity();
		void reserve(size_t n);
		void resize(size_t n, char ch = '\0');
		bool empty()const;

		//修改字符串相关函数
		void push_back(char ch);
		void append(const char* str);
		string& operator+=(char ch);
		string& operator+=(const char* str);
		string& insert(size_t pos, char ch);
		string& insert(size_t pos, const char* str);
		string& erase(size_t pos, size_t len);
		void clear();
		void swap(string& s);
		const char* c_str()const;

		//访问字符串相关函数
		char& operator[](size_t i);
		const char& operator[](size_t i)const;
		size_t find(char ch, size_t pos = 0)const;
		size_t find(const char* str, size_t pos = 0)const;
		size_t rfind(char ch, size_t pos = npos)const;
		size_t rfind(const char* str, size_t pos = 0)const;

		//关系运算符重载函数
		bool operator>(const string& s)const;
		bool operator>=(const string& s)const;
		bool operator<(const string& s)const;
		bool operator<=(const string& s)const;
		bool operator==(const string& s)const;
		bool operator!=(const string& s)const;

	private:
		char* _str;       //存储字符串
		size_t _size;     //记录字符串当前的有效长度
		size_t _capacity; //记录字符串当前的容量
		static const size_t npos; //静态成员变量(整型最大值)
	};
	const size_t string::npos = -1;

	//<<和>>运算符重载函数
	istream& operator>>(istream& in, string& s);
	ostream& operator<<(ostream& out, const string& s);
	istream& getline(istream& in, string& s);
}

注意:为了防止标准库当中的string类产生命名冲突,模拟实现时需放在自己的命名空间当中。 

默认成员函数

  构造函数

        构造函数设置为缺省参数,若不传入参数,则默认构造为空字符串。字符串的初始大小和容量均设置为传入C字符串的长度(不包括’\0’)

//构造函数
string(const char* str = "")
{
	_size = strlen(str); //初始时,字符串大小设置为字符串长度
	_capacity = _size; //初始时,字符串容量设置为字符串长度
	_str = new char[_capacity + 1]; //为存储字符串开辟空间(多开一个用于存放'\0')
	strcpy(_str, str); //将C字符串拷贝到已开好的空间
}

  拷贝构造函数

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

浅拷贝(Shallow Copy):

        当执行浅拷贝时,如果对象中包含指针变量,新旧对象将共享同一个指针所指向的内存空间。这意味着,如果其中一个对象修改了通过该指针所指向的数据,这种改变对于另一个对象也是可见的,因为他们实际上指向的是同一块内存。这对于只包含基本数据类型(如int、float、char等)的对象没有问题,但对于包含指针(指向动态分配的内存或其他复杂数据结构)的对象,则可能导致意料之外的行为。

深拷贝(Deep Copy):

        深拷贝不仅会复制对象本身,还会递归地复制对象中指针所指向的所有内容,为源对象中指针所指向的每个对象分配新的内存空间。这样,即使在拷贝之后修改其中一个对象的数据,也不会影响到另一个对象。它们拥有各自独立的数据副本,确保了对象间的完全独立性。

        下面提供深拷贝的两种写法: 

传统写法:

步骤概述:

  1. 分配内存:在拷贝构造函数或赋值运算符中,首先为新对象(即拷贝对象)动态分配足够的内存来容纳源对象字符串的内容。这是通过new操作符完成的,例如:_str = new char[sourceSize],其中sourceSize包括源字符串的长度加上结束符\0

  2. 复制内容:接下来,逐字符地将源对象中的字符串内容复制到新分配的内存空间中。这可以通过循环或使用诸如strcpy之类的库函数来实现。例如:strcpy(_str, source->_str)

  3. 复制其他成员变量:除了字符串内容外,还需要将源对象的其他成员变量(如_size_capacity)复制到新对象中,保证新对象的这些状态信息与源对象一致。

  4. 处理源对象的特殊状况:在某些情况下(尤其是在赋值运算符重载中),可能还需要考虑源对象自身可能需要释放原有的内存,以防止内存泄漏。

//传统写法
string(const string& s)
	:_str(new char[s._capacity + 1])
	, _size(0)
	, _capacity(0)
{
	strcpy(_str, s._str);    //将s._str拷贝一份到_str
	_size = s._size;         //_size赋值
	_capacity = s._capacity; //_capacity赋值
}

 现代写法:

步骤概述:
  1. 构造临时对象(tmp): 在构造函数内部,首先根据传入的C字符串参数,创建一个局部临时的string对象(tmp)。这里,我们会利用现有的构造函数(可能是接受C字符串参数的构造函数),确保深拷贝字符串内容。

  2. 交换数据: 然后,设计一个swap成员函数来交换两个string对象的数据成员(_str_size_capacity等)。在构造函数内部调用这个swap函数,将临时对象tmp的数据与正在构造的对象的数据交换。

  3. 临时对象自动销毁: 构造完成后,局部临时对象tmp会自动销毁,由于数据已被交换到新对象中,因此不会导致资源泄露。

//现代写法
string(const string& s)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(s._str); //调用构造函数,构造出一个C字符串为s._str的对象
	swap(tmp); //交换这两个对象
}

  赋值运算符重载函数

        与拷贝构造函数类似,赋值运算符重载函数的模拟实现也涉及深浅拷贝问题,我们同样需要采用深拷贝。

        下面也提供深拷贝的两种写法:

传统写法:

步骤概述:

  1. 自我赋值检测:首先检查是否是自我赋值情况(this == &s),如果是,则直接返回*this,避免无用操作。
  2. 释放资源:释放当前对象(左值)已有的动态分配资源,比如释放_str指向的内存。
  3. 资源分配:根据源对象(右值,即赋值操作数)的大小,重新为左值对象分配必要的内存空间。
  4. 深拷贝内容:将源对象的字符串内容及其他数据成员逐个复制到新分配的内存中。
  5. 完成:返回左值引用(*this),允许链式赋值操作。
//传统写法
string& operator=(const string& s)
{
	if (this != &s) //防止自己给自己赋值
	{
		delete[] _str; //将原来_str指向的空间释放
		_str = new char[s._capacity + 1]; //重新申请一块空间
		strcpy(_str, s._str);    //将s._str拷贝一份到_str
		_size = s._size;         //_size赋值
		_capacity = s._capacity; //_capacity赋值
	}
	return *this; //返回左值(支持连续赋值)
}

  现代写法:

步骤概述:

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值