【c++】string的模拟实现

目录

 一.  交换函数swap

二.  默认成员函数 

     构造函数和析构函数

     拷贝构造函数和赋值运算符重载

三.  容量相关操作接口

     size 与 capacity

     reserve 与 resize

附:reserve与resize的区别

四.  修改相关操作接口

    push_pack 

    append   

    insert 与 erase   

    operator+=

    find 

   substr

   clear

五.  遍历访问相关接口

   使用迭代器

  operator[ ] 

 六.  非成员函数流插入和流提取运算符重载 (<<、>>)

附:完整代码实现


示:string相关的操作接口有很多,这里实现了部分常用接口


                          

   

 一.  交换函数swap

           在实现string之前先提供以下三个交换函数,如下:

   

//用于交换的交换函数模板
template  
void swap(T& a, T& b)
{
	T tmp = a;
	a = b; 
	b = tmp;
}

    这是一个全局的交换函数,是一个泛型版本。此交换函数被定义在库当中,即头文件为的标准算法库里但是,如果是交换一般的数据还是可以的;若是想要交换一个存储大量数据的类内容时,因这个交换函数涉及一个复制构造和两个赋值操作,此函数并不是最有效方法。

此时有另外两个交换函数:

  这个交换函数是string当中的,是一个非成员函数;其实这是上面通用算法交换的重载,它通过相互转移对其内部数据的所有权来提高其性能即,字符串交换对其数据的引用,而不实际复制字符


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

    该交换函数使用行为类似于此成员函数的优化重载 ; 这是为处理大型数据类型提供此函数的重载版本,优化其性能,即只交换几个内部指针而不是它们的全部内容,从而使它们在恒定时间内运行。

说明:以下string的有关拷贝构造赋值操作将用到以上的交换函数。

二.  默认成员函数 

       构造函数和析构函数

构造函数:①写两个构造函数(无参和带参);②直接写一个带缺省参数的构造函数(本次采                        用这个)         

  带缺省参数的构造函数:   

    缺省参数置为空字符串

析构函数:

~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

拷贝构造函数和赋值运算符重载

拷贝构造:一个已存在的对象去拷贝初始化另一个对象。

首先先拷贝构造一个临时对象,在复用上面开始时所说的交换函数,交换两者指针即可


string(const string& s)
{
	string tmp(s._str); //拷贝构造临时对象tmp
	swap(tmp);        //交换两者数据,只需要交换两者指针即可
}

【c++】string的模拟实现_第1张图片

赋值运算符重载:将两个已存在的对象进行拷贝——赋值重载

string& operator=(string s) //注意这里不要加引用&
{
	swap(s);
	return *this;
}

【c++】string的模拟实现_第2张图片

说明:参数中不加引用的目的就是不改变原对象的中的内容。若加了引用就会改变原对象的内容,也即将s1与s2直接进行交换;这样虽然将s1赋给了s2,但s1本身也改了,这就不符合我们的需求。

三.  容量相关操作接口

       size 与 capacity

 这是符合字符串内容的实际字节数,不一定等于其存储容量 ;返回字符串的有效数据长度(以字节为单位)。

     

size_t size() const
{
	return _size;
}

返回当前为字符串分配的存储容量的大小容量不一定等于字符串长度,它可以相等或更大。

size_t capacity() const
{
	return _capacity;
}

reserve 与 resize

reserve : 如果 n 大于当前字符串容量,则该函数会导致容器将其容量增加到 n 个字符(或更大)

此函数对字符串长度没有影响,并且无法更改其内容;但若 n 小于当前字符串容量,有些平台会缩容(linux),但是不会影像数据;有些不会(vs),取决于编译器,即总的来说,该函数会影响容量,但不会影响有效数据

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1]; //开辟新的空间
		strcpy(tmp, _str); //拷贝原数据到新空间
		delete[] _str;   //释放原空间
		_str = tmp;
		_capacity = n; //更新容量
	}
}

resize:将字符串大小调整为 n 个字符的有效长度。
如果 n 小于当前字符串长度,则当前值将缩短为其前 n 字符,并删除第 n 个字符以外的字符。
如果 n 大于当前字符串长度,则通过在末尾插入任意数量的字符来扩展当前内容,以达到 n 的大小。

如果指定了 c,则新元素将初始化为 c 的副本,否则,它们是值初始化的字符(null字符)。

void string::resize(size_t n, char c)
{
    //n小于元素的个数
	if (n < _size)
	{
		_size = n;
		_str[_size] = '\0'; //末尾处理\0
	}
	else 
	{
        //n大于当前容量
		if (n > _capacity)
        {
			reserve(n);    //扩容
        }
		for (size_t i = _size; i < n; i++)//将size个元素后的填充为字符c
		{
			_str[i] = c;
		}
		_str[n] = '\0';
		_size = n;
	}
}

附:reserve与resize的区别

reserve用于预留空间,但不会改变容器的大小,只是增加容器的容量,如果超过容器的容量,会重新分配内存

resize则是直接改变容器的大小,如果改变后的大小比原来的小,会删掉多余的元素,如果比原来的大,会添加默认值的元素。

总之:reserve 改变容器的容量——对应capacity

           resize 改变容器的大小——对应size,resize后 都为 有效元素

四.  修改相关操作接口

       push_pack 

 push_back:将字符 c 追加到字符串的末尾,使其长度增加 一。

void push_back(char c)
{
	if (_size == _capacity) //检查容量
	{
		size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
		reserve(newcapacity);
	}
	_str[_size] = c;
	_size++;
	_str[_size] = '\0'; //注意处理末尾\0
}

 append

  

 功能:在字符串当前值的末尾追加一个字符串

string& append(const char* str)
{
	size_t len = strlen(str);    //计算追加字符串的长度
	if (_size + len > _capacity) //检查容量
	{
		reserve(_size + len);
	}
	strcpy(_str + _size, str);  //把要追加的字符串追加到该字符串的末尾
	_size += len;
    return *this;
}

   

insert 与 erase 

insert在pos位置插入一个字符

// 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c)
{
	assert(pos <= _size);
	if (_size == _capacity) //检查容量
	{
		size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
		reserve(newcapacity);
	}
	//注意size_t为无符号;这种写法程序会崩
	/*size_t end = _size;
	while (end >= pos)           
	{
		_str[end + 1] = _str[end];
		--end;
	}*/
	size_t end = _size + 1;  
	while (end > pos)         //pos位置后元素向后挪
	{
		_str[end] = _str[end - 1];
		--end;
	}
	_str[pos] = c;
	_size++;
	return *this;
}

【c++】string的模拟实现_第3张图片

           

insert在pos位置插入一个字符串

string& insert(size_t pos, const char* str)
{
	assert(pos <= _size);

	size_t len = strlen(str);      //计算原字符串的长度
	if (_size + len > _capacity)   //检查容量
	{
		reserve(_size + len);
	}
	size_t end = _size + 1;
	while (end > pos)              //pos后的元素向后挪动(end+len)个长度
	{
		_str[end + len - 1] = _str[end - 1];
		--end;
	}
	strncpy(_str + pos, str, len);  //注意这里不要用strcpy
	_size += len;
	return *this;
}

说明:①挪动数据时从\0 开始挪动 (即 \0 也要挪动)

           ②这里不使用strcpy 而 使用strncpy,  是因为strcpy会把 \0 也拷贝进去,这样拷贝后有效数据元素的个数(即size)就不一定对了。

    erase 

功能:删除字符串值中从字符位置 pos 开始并跨越 len 个字符的部分(如果内容太短            或 len 为 nops,则删除直到字符串末尾的部分。

注意:如果不写len(即为默认参数),则会删除pos往后的所有字符(有多少删多少) 

// 删除pos位置上的元素,并返回该元素的下一个位置
	string& erase(size_t pos, size_t len)
	{
		assert(pos < _size);

		if (len == npos || pos + len >= _size)
		{
			_str[pos] = '\0';
			_size = pos;
			return *this;
		}
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
		return *this;
	}

          图解 :

     【c++】string的模拟实现_第4张图片

    operator+=

  

   功能:在字符串当前值的末尾追加一个字符

string& operator+=(char c)
{
	push_back(c); //复用push_back即可
	return *this;
}

功能:在字符串当前值的末尾追加一个字符串

string& operator+=(const char* str)
{
	append(str);  //复用append即可
	return *this;
}

     find 

功能:从指定的pos位置处开始(若不指定默认从第一个字符开始)查找一个匹配的字符c

返回值:返回c在字符串中第一次出现的位置

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

                      

功能:从指定的pos位置处开始(若不指定默认从第一个字符开始)查找一个匹配的字符串

返回值:第一个匹配项的第一个字符的位置。
              如果未找到匹配项,该函数将返回 string::npos

// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos) const
{
	const char* ptr = strstr(_str + pos, s);
	if (ptr == nullptr)
		return npos;
	return ptr - _str;
}

     substr

 返回值:返回一个新构造的对象,其值初始化为此对象的子字符串的副本   

string substr(size_t pos, size_t len)
{
	assert(pos < _size);

	size_t end = len + pos;
	if (len == npos || pos + len >= _size)
	{
		end = _size;
	}
	string str;
	str.reserve(end - pos);
	for (size_t i = pos; i < end; i++)
	{
		str += _str[i];
	}
	return str;
}

     图解:     【c++】string的模拟实现_第5张图片

   clear

         功能:擦除字符串的内容,该字符串将变为空字符串(长度为 0 个字符)。

void clear()
{
	_str[0] = '\0'; //注意首字符置为\0
	_size = 0;
}

五.  遍历访问相关接口

       使用迭代器

 begin:

功能:如果字符串对象是 const 限定的,则该函数返回const_iterator。否则,它将返回一个迭代器。         

//返回iterator迭代器 
iterator begin()
{
	return _str;
}

//返回const_iterator迭代器
const_iterator begin() const
{
	return _str;
}

end:

功能:如果字符串对象是 const 限定的,则该函数返回const_iterator。否则,它将返回一个迭代器。

//返回iterator 迭代器
iterator end()
{
	return _str + _size;
}

//返回const_iterator 迭代器
const_iterator end() const
{
	return _str + _size;
}

operator[ ] 

   

char& operator[](size_t pos)
{
	assert(pos <= _size);
	return _str[pos];
}

const char& operator[](size_t pos) const
{
	assert(pos <= _size);

	return _str[pos];
}

 六.  非成员函数流插入和流提取运算符重载 (<<、>>)

  <<:       功能:将符合 str 值的字符序列插入到 os 中。  

ostream& operator<<(ostream& out, const string& s)
{
	for (auto ch : s)
	{
		out << ch;
	}
	return out;
}

>>:

功能:从输入流中提取字符串,将序列存储在 str 中,该序列被覆盖(替换了 str 的先前值)。

注意:输入流提取操作使用空格作为分隔符(在获取字符时会自动忽略空格),因此操作将仅从中提取可视为单词的内容

istream& operator>>(istream& in, string& s)
{
	char ch = in.get(); //这里用get()获取字符(包括空格),直到遇到换行结束
	while (ch != ' ' && ch != '\0')
	{
		s += ch;
		ch = in.get();
	}
	return in;
}

附:完整代码实现

#include 
#include 
using namespace std;

namespace mystring
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
	public:
		
		string(const char* str = "");
		~string();

		string(const string& s);
		string& operator=(const string& s);
		
		void resize(size_t n, char c);
		void resize(size_t n);
		void reserve(size_t n);

		void push_back(char c);
		string& operator+=(char c);
		string& operator+=(const char* str);
		void append(const char* str);

		void clear();
		void swap(string& s);


		char& operator[](size_t pos);
		const char& operator[](size_t pos) const;

		size_t find(char c, size_t pos = 0) const;       
		size_t find(const char* s, size_t pos = 0) const; 
		string substr(size_t pos = 0, size_t len = npos);
		string& insert(size_t pos, char c);              
		string& insert(size_t pos, const char* str);
		string& erase(size_t pos, size_t len = npos);     

	private:
		char* _str;
		size_t _size;
		size_t _capacity;

		const static size_t npos = -1;
	};

    
    string::string(const char* str)
	{
		_size = strlen(str);
		_capacity = _size;
		_str = new char[_capacity + 1];

		strcpy(_str, str);
	}
	
	string(const string& s)
	{
		string tmp(s._str);
		swap(tmp);
	}

	string& operator=(string s)
	{
		swap(s);
		return *this;
	}

	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = _capacity = 0;
	}

    // 容量
	size_t size() const
	{
		return _size;
	}

    size_t capacity() const
    {
	    return _capacity;
    }

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

	void string::resize(size_t n, char c)
	{
		if (n < _size)
		{
			_size = n;
			_str[_size] = '\0';
		}
		else 
		{
			if (n > _capacity)
				reserve(n);
			for (size_t i = _size; i < n; i++)
			{
				_str[i] = c;
			}
			_str[n] = '\0';
			_size = n;
		}
	}

	void string::resize(size_t n)
	{
		if (n < _size)
		{
			_size = n;
			_str[_size] = '\0';
		}
		else
		{
			if (n > _capacity)
				reserve(n);
			for (size_t i = _size; i < n; i++)
			{
				_str[i] = '\0';
			}
			_size = n;
		}
	}

	// 修改
	void string::push_back(char c)
	{
		if (_size == _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newcapacity);
		}
		_str[_size] = c;
		_size++;
		_str[_size] = '\0';
	}

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

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

	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len);
		}
		strcpy(_str + _size, str);
		_size += len;
	}

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

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

	// 访问
	iterator begin()
	{
		return _str;
	}

	const_iterator begin() const
	{
		return _str;
	}

	iterator end()
	{
		return _str + _size;
	}

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

	char& string::operator[](size_t pos)
	{
		assert(pos <= _size);
		return _str[pos];
	}

	const char& string::operator[](size_t pos) const
	{
		assert(pos <= _size);

		return _str[pos];
	}

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

	// 返回子串s在string中第一次出现的位置
	size_t string::find(const char* s, size_t pos) const
	{
		const char* ptr = strstr(_str + pos, s);
		if (ptr == nullptr)
			return npos;
		return ptr - _str;
	}

	string string::substr(size_t pos, size_t len)
	{
		assert(pos < _size);
		size_t end = len + pos;
		if (len == npos || pos + len >= _size)
		{
			end = _size;
		}
		string str;
		str.reserve(end - pos);
		for (size_t i = pos; i < end; i++)
		{
			str += _str[i];
		}
		return str;
	}

	// 在pos位置上插入字符c/字符串str,并返回该字符的位置
	string& string::insert(size_t pos, char c)
	{
		assert(pos <= _size);
		if (_size == _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(newcapacity);
		}
		size_t end = _size + 1;
		while (end > pos)
		{
			_str[end] = _str[end - 1];
			--end;
		}
		_str[pos] = c;
		_size++;
		return *this;
	}

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

	// 删除pos位置上的元素,并返回该元素的下一个位置
	string& string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);
		if (len == npos || pos + len >= _size)
		{
			_str[pos] = '\0';
			_size = pos;
			return *this;
		}
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
		return *this;
	}
    
    //流插入、流提取
	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}

	istream& operator>>(istream& in, string& s)
	{
		char ch = in.get();
		while (ch != ' ' && ch != '\0')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}

}


你可能感兴趣的:(《C++面向对象》,c++,开发语言)