【C++进阶】STL-string的简单实现

目录

1.构造函数&析构函数

[2. 拷贝构造函数与"="](#2. 拷贝构造函数与“=”)

3.简易版STL-string代码

[4. 增删查改版STL-string](#4. 增删查改版STL-string)

resize详解:

erase详解:

完整代码&测试


1.构造函数&析构函数

想要实现下面的代码,我们该如何编写构造函数?

cpp 复制代码
void test_string1()
{
	string s1("hello");
	string s2;
}

string本身其实只需要一个字符指针;构造函数使用全缺省的一个写法,我们可以这么写:

string.h

cpp 复制代码
#pragma once
#include<iostream>
namespace luo_string
{
	class string
	{
	private:
		char* _str;

	public:
		// 全缺省构造函数
		string(const char* str = "")
			:_str(new char[strlen(str) + 1])
		{
			strcpy(_str, str);
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
		size_t size()
		{
			return strlen(_str);
		}
		char& operator[](size_t i) // 返回引用,能修改
		{
			return _str[i];
		}
	};


	void test_string1()
	{
		string s1("hello");
		string s2;
	}

	
}

①首先为了避免命名冲突,我们可以写一个自己的命名空间,所有代码写在这个命名空间之内;

②接下来看构造函数,需要传入一个字符串,也就是const char* 类型的数据,所以需要用此类型作为形参,如果没有传入任何参数,这里就默认给"",也就是空字符串,这里的空字符串包含一个字符也就是\0,这里给_str分配空间使用strlen对传入的数据计算 + 1,最后使用strcpy拷贝数据;

③析构函数就非常简单,直接将_str的空间释放,_str给一个nullptr,避免野指针;

2. 拷贝构造函数与"="

如果不实现拷贝构造函数,那么下面的操作会调用系统默认的拷贝构造函数,那么就会出现问题;

cpp 复制代码
void test_string2()
{
	string s1("hello");
	string s2(s1);
}

系统默认的构造函数是浅拷贝,也就是说,两个指针指向同一块内存地址;析构函数会调用两次;

具体流程:

s1构造------》s2构造

s2析构------》s1析构

s2析构的时候先将内存释放,再将s2置空;

s1此时仍然指向那个被释放的空间,就会再次对内存空间释放,就会产生报错;

cpp 复制代码
// 拷贝构造函数
string(const string& s) 
	:_str(new char[strlen(s._str) + 1])
{
	strcpy(_str,s._str);
}

那么赋值运算符"=",也有这样的特点;

这里重载一下赋值运算符就可以了;这里需要注意内存泄露的问题,例如s1 = s3,这里需要将s1之前的老空间进行释放

需要给一个额外的空间,将数据传入之后,需要将this指向的老空间释放,再指向新空间;

如果出现自己 = 自己的情况,会将这个流程跳过,直接返回自己

cpp 复制代码
string& operator=(const string& s)
{
	// 防止自己 = 自己
	if (this != &s) 
	{
		char* tmp = new char[strlen(s._str) + 1];
		strcpy(tmp, s._str);
		delete[] _str;
		_str = tmp;
	}
	return *this;
}

3.简易版STL-string代码

cpp 复制代码
#pragma once
#include<iostream>
namespace luo_string
{
	class string
	{
	private:
		char* _str;

	public:
		// 有参和无参的构造函数

		// 全缺省构造函数
		string(const char* str = "")
			:_str(new char[strlen(str) + 1])
		{
			strcpy(_str, str);
		}

		// 拷贝构造函数
		string(const string& s)
			:_str(new char[strlen(s._str) + 1])
		{
			strcpy(_str, s._str);
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
		}
		size_t size()
		{
			return strlen(_str);
		}
		char& operator[](size_t i) // 返回引用,能修改
		{
			return _str[i];
		}

		string& operator=(const string& s)
		{
			// 防止自己 = 自己
			if (this != &s) 
			{
				char* tmp = new char[strlen(s._str) + 1];
				strcpy(tmp, s._str);
				delete[] _str;
				_str = tmp;
			}
			return *this;
		}

	};


	void test_string1()
	{
		string s1("hello");
		string s2;
	}

	void test_string2()
	{
		string s1("hello");
		string s2(s1);
		string s3 = s2;
	}

}

4. 增删查改版STL-string

resize详解:

需求:需要将string设置成指定的size大小,使用指定的字符进行填充

假设:此时的size = 5,capacity = 10;

情况一:将size设置成2,直接将2这个位置填充为'\0',size = 2即可;

情况二:将size设置成8,从size开始到8之前的位置全部替换为ch;,8的位置替换为'\0';

情况三:将size设置成18,先扩容,将capacity变成18,再进行替换操作,同情况二;

cpp 复制代码
void resize(size_t n,char ch = '\0')
{
	if (n < _size) 
	{
		_str[n] = '\0';
		_size = n;
	}
	else 
	{
		if (n > _capacity)
		{
			reserve(n);
		}

		// 写数据
		for (size_t i = _size; i < n; i++)
		{
			_str[i] = ch;
		}
		_size = n;
		_str[_size] = '\0';
	}
}

erase详解:

需求:将指定的位置删除指定的长度

例如:helloword\0,删除pos=5位置,size = 2、5、8个长度;

情况一:从下标5,删除2个元素,其实就是下标为pos + len的元素往前覆盖到pos的位置,被覆盖的下标和覆盖的下标依次++;

情况二、三:删除的len + pos > _size,说明要全部删除完毕;如果不传默认参数为string::npos为整数最大值;此时只需要将pos置为'\0'即可,最后将size变为size - len即可;

cpp 复制代码
void erase(size_t pos, size_t len = npos) 
{
	if (len + pos >= _size) // 说明此处到后面直接删完
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		size_t i = pos + len;
		// 此处的位置往前挪一位
		for ( ; i <= _size; i++)
		{
			_str[i - len] = _str[i];
		}
		_size -= len;
	}
};

完整代码&测试

cpp 复制代码
#pragma once
#include<iostream>
#include<assert.h>
//namespace luo_string
//{
//	class string
//	{
//	private:
//		char* _str;
//
//
//	public:
//		// 全缺省构造函数
//		string(const char* str = "")
//			:_str(new char[strlen(str) + 1])
//		{
//			strcpy(_str, str);
//		}
//
//		// 拷贝构造函数
//		string(const string& s)
//			:_str(new char[strlen(s._str) + 1])
//		{
//			strcpy(_str, s._str);
//		}
//
//		~string()
//		{
//			delete[] _str;
//			_str = nullptr;
//		}
//		size_t size()
//		{
//			return strlen(_str);
//		}
//		char& operator[](size_t i) // 返回引用,能修改
//		{
//			return _str[i];
//		}
//
//		string& operator=(const string& s)
//		{
//			// 防止自己 = 自己
//			if (this != &s) 
//			{
//				char* tmp = new char[strlen(s._str) + 1];
//				strcpy(tmp, s._str);
//				delete[] _str;
//				_str = tmp;
//			}
//			return *this;
//		}
//
//	};
namespace luo_string
{
	typedef char* iterator;
	class string
	{
		friend std::ostream& operator<<(std::ostream& out, const string& s);
		friend std::istream& operator>>(std::istream& in, string& s);
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
		static size_t npos;

	public:

		// 全缺省构造函数
		string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		// 拷贝构造函数
		string(const string& s)
			:_str(new char[strlen(s._str) + 1])
		{
			strcpy(_str, s._str);
			_capacity = s._capacity;
			_size = s._size;
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _capacity;
		}
		char& operator[](size_t i) // 返回引用,能修改
		{
			assert(i < _size);
			return _str[i];
		}

		const char& operator[](size_t i)const // const对象的重载
		{
			assert(i < _size);
			return _str[i];
		}

		string& operator=(const string& s)
		{
			// 防止自己 = 自己
			if (this != &s)
			{
				char* tmp = new char[strlen(s._str) + 1];
				strcpy(tmp, s._str);
				delete[] _str;
				_str = tmp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}

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

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

		// 插入一个字符
		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
				reserve(newcapacity);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}

		// 插入一个字符串
		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str);
			_size += len;
		}

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

		string& operator+=(const char* str)
		{
			this->append(str);
			return *this;
		}
		// 插入单个字符
		void insert(size_t pos, char ch)
		{
			assert(pos < _size);
			if (_size == _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
				reserve(newcapacity);
			}
			size_t end = _size;
			// 从后往前挪
			while (pos <= end)
			{
				_str[end + 1] = _str[end]; // '\0'放到后面,以此类推
				--end;
			}
			_str[pos] = ch;
			++_size;
		}
		// 插入多个字符
		void insert(size_t pos, const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			size_t end = _size;
			while (pos <= end)
			{
				// 每一个值[len]下一个值
				_str[end + len] = _str[end];
				--end;
			}
			// 赋值
			/*for (size_t i = 0; i < len; i++)
			{
				_str[pos+i] = str[i];
			}*/
			// 这里不要使用strcpy会将\0一起拷贝
			strncpy(_str + pos, str, len);
			_size += len;
		}
		// 将size大小变为n
		/*
			_size = 5,_capacity = 10
			情况1:变为8
			情况2:变为18
			情况3:变为2
		*/
		void resize(size_t n, char ch = '\0')
		{
			if (n < _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				if (n > _capacity)
				{
					reserve(n);
				}

				// 写数据
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_size = n;
				_str[_size] = '\0';
			}
		}
		void erase(size_t pos, size_t len = npos)
		{
			if (len + pos >= _size) // 说明此处到后面直接删完
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t i = pos + len;
				// 此处的位置往前挪一位
				for (; i <= _size; i++)
				{
					_str[i - len] = _str[i];
				}
				_size -= len;
			}
		};
		size_t find(char ch, size_t pos = 0)
		{
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch) {
					return i;
				}
			}
			return string::npos;
		}
		// 查找子串
		size_t find(const char* str, size_t pos = 0)
		{
			// 直接调用c语言库,返回这个位置的指针
			char* p = strstr(_str, str);
			if (p == nullptr)
			{
				return string::npos;
			}
			else
			{
				return (p - _str);// 返回这个元素的下标
			}
		}

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

		bool operator==(const string& s)
		{
			int ret = strcmp(this->_str, s._str);
			return ret == 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 || *this == s;
		}
		bool operator!=(const string& s)
		{
			return !(*this == s);
		}


	};
	size_t string::npos = -1; // -1给size_t就是整数最大值
	std::ostream& operator<<(std::ostream& out, const string& s)
	{
		out << s._str;
		return out;
	}

	std::istream& operator>>(std::istream& in, string& s)
	{
		char ch;
		//in >> ch; // 拿不到回车
		while (1)
		{
			ch = in.get();
			if (ch == ' ' || ch == '\n')
			{
				break;
			}
			else {
				s += ch;
			}
		}
		return in;
	}









	void test_string1()
	{
		string s1("hello");
		string s2;
	}

	void test_string2()
	{
		string s1("hello");
		string s2(s1);
		string s3 = s2;
		luo_string::iterator it = s1.begin();
		while (it != s1.end())
		{
			std::cout << *it << " ";
			++it;
		}

		// 本身没有对范围for做支持,编译器会把范围for直接转换为迭代器
		for (auto e : s1)
		{
			std::cout << e;
		}

		s1.push_back('a');
		s1.append("what the fuck\n");
		// 本身没有对范围for做支持,编译器会把范围for直接转换为迭代器
		s1 += "hello world\n";
		s1 += 'y';
		for (auto e : s1)
		{
			std::cout << e;
		}
	}
	void test_string3()
	{
		string s1("hello");
		s1.insert(2, "fuck");
		for (auto e : s1)
		{
			cout << e;
		}
	}


	void test_string4()
	{
		string s1 = "hello";
		s1.reserve(10); // 容量变成10
		cout << s1 << endl;
		cout << s1.size() << endl;
		cout << s1.capacity() << endl << endl;

		s1.resize(8, 'c');
		cout << s1 << endl;
		cout << s1.size() << endl;
		cout << s1.capacity() << endl << endl;

		s1.resize(2, 'b');
		cout << s1 << endl;
		cout << s1.size() << endl;
		cout << s1.capacity() << endl << endl;

		s1.resize(18, 'a');
		cout << s1 << endl;
		cout << s1.size() << endl;
		cout << s1.capacity() << endl;
	}


	void test_string5()
	{
		string s1 = "abcdef";
		cout << s1.find("bcd") << endl;
		cout << s1.find("bed") << endl;

		string s2;
		cin >> s2;
		cout << s2 << endl;
	}

}
相关推荐
..空空的人2 小时前
C++基于websocket的多用户网页五子棋 --- 项目设计
c++·个人开发
superlls2 小时前
(Java基础)集合框架继承体系
java·开发语言
ad钙奶长高高2 小时前
【C语言】原码反码补码详解
c语言·开发语言
奔跑吧邓邓子2 小时前
【C语言实战(73)】深入C语言网络编程:UDP与TCP的实战对决
c语言·udp·网络编程·tcp·开发实战
wefg12 小时前
【数据结构】红黑树
数据结构·算法
咪咪渝粮2 小时前
112.路径总和
java·数据结构·算法
IoT智慧学堂2 小时前
C语言运算符与表达式详解——算术、逻辑与赋值的全面理解
c语言·开发语言
电子_咸鱼3 小时前
高阶数据结构——并查集
数据结构·c++·vscode·b树·python·算法·线性回归