带你深入了解string类接口(传统写法实现string类)

Posted 两片空白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带你深入了解string类接口(传统写法实现string类)相关的知识,希望对你有一定的参考价值。

目录

前言

一.构造,析构函数和赋值操作符的重载

二.容量函数,operator[]重载函数和c_str函数

三.resize和reserve函数

四.插入和删除函数

五.查找函数和比较函数

六.operator<<和operator>>操作符重载

七.完整代码


前言

在博客C++ string类常用接口一文中我们熟知了C++string类的一些常用接口的使用,在本文时来简单实现一下这些常用的接口,带你了解string类的底层原理,然你能轻而易举的掌握string类的使用。

说明注意点:在我们自己实现string类时,如果你的类名用的与标准库类名相同时,需要一个命名空间将其与标准库里的string类分开。

下面是代码的分开实现,完整代码在最下面给出。

一.构造,析构函数和赋值操作符的重载

//要在堆上开辟空间将字符串拷贝过去,不能直接在初始化列表里初始化,否则不可修改
		//空字符串也要开辟一个空间,放\\0
		//拷贝构造函数
		string(const char *str="")
		:_str(new char[strlen(str)+1])
		{
			strcpy(_str, str);
			_size = strlen(str);
			_capacity = _size;
		}
		//拷贝构造函数
		string(const string& s){
			_str = new char[strlen(s._str) + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
			
		}
		//析构函数
		~string(){
			delete[] _str;
			_str = nullptr;
		}
		//赋值操作符
		string& operator=(const string& s){
			if (this != &s){
				char *newstr = new char[strlen(s._str) + 1];
				strcpy(newstr, s._str);
                //需要delete之前的——str空间,避免内存泄漏
				delete[] _str;
				_str = newstr;
				_size = s._size;
				_capacity = s._capacity;
				
			}
			return *this;
		}

注意拷贝构造和赋值操作符重载函数,这里蕴含着深浅拷贝问题。如果不定义着两个函数,编译器会自动生成,但是仅仅只是按字节进行拷贝,也就是值拷贝/浅拷贝。在上面函数中,如果只是进行浅拷贝,两字符指针变量指向堆上的同一块空间。当对象出作用域时,调用析构函数,但是同一块空间不能释放两次,会导致程序奔溃。

解决方法就是使用深拷贝

再堆上再申请一块空间,将拷贝值或者赋值值copy过去。将需要被赋值或者被拷贝的对象的字符指针指向新申请的空间。

二.容量函数,operator[]重载函数和c_str函数

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

		size_t size()const{
			return _size;
		}
		size_t capacity()const{
			return _capacity;
		}
		const char *c_str(){
			return _str;
		}

三.resize和reserve函数

//不需要返回值,在函数体里this改变了
//三种情况,
//len小于_size,
//len大于_szie小于_capacity
//len大于_capacity
		void resize(size_t len, char c = '\\0'){
			if (len > _size){
				if (len > _capacity){
					char *temp = new char[len + 1];
					strcpy(temp, _str);
					delete[] _str;
					_str = temp;
					_capacity = len;
				}
				//用memset函数,或者用循环
				memset(_str + _size, c, len - _size);

			}
			_size = len;
			_str[_size] = '\\0';
		}
		void reserve(size_t newcapacity){
			//只有大于容量时才扩容
			if (newcapacity > _capacity){
				char *newstr = new char[newcapacity + 1];
				strcpy(newstr, _str);
				delete[] _str;
				_str = newstr;
				_capacity = newcapacity;

			}
		}

四.插入和删除函数

插入时注意是否要扩容。

再中间插入要将后面的往后移,再中间删除要将后面的往前移。

		void push_back(char c){
			if (_size == _capacity){
				//细节 判断容量是否等于0
				size_t newcapacity = _capacity == 0 ? 2 : 2 * _capacity;
				//加1  预留\\0位置
				char *newstr = new char[newcapacity+1];
				strcpy(newstr, _str);
				delete[] _str;
				_str = newstr;
				_capacity = newcapacity;
			}
			_str[_size++] = c;
			//要在末尾加上\\0
			_str[_size] = '\\0';
			
		}

		void append(const char *s){
			size_t len = strlen(s) + _size;
			if (len > _capacity){
				char *newstr = new char[len + 1];
				strcpy(newstr, _str);
				delete[] _str;
				_str = newstr;
				_capacity = len;
			}
			strcpy(_str + _size, s);
			_size = len;
			
		}
		//复用push_back
		string& operator+=(char c){
			push_back(c);
			return *this;
		}
		//服用append
		string& operator+=(const char *s){
			append(s);
			return *this;
		}
        string& insert(size_t pos, char c){
			assert(pos < _size);
				if (_size==_capacity){
					int newcapacity = _capacity == 0 ? 2 : 2*_capacity;
					reserve(newcapacity);
				}
				size_t end = _size;
				while (end >= pos){
					_str[end + 1] = _str[end];
					end--;
				}
				_str[pos] = c;
				_size++;

				return *this;

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

		}
		//两种情况,删除长度加上位置小于_size,删除长度加上位置大于等于_size。
		string& erase(size_t pos, size_t len = npos){
			assert(pos < _size);
			//用减比较好,防止越界,加的话npos最大值,加上一值容易越界
			if (len >= _size - pos){
				_str[pos] = '\\0';
				_size = pos;
			}
			else{
				size_t i = pos + len;
				while (i <= _size){
					_str[pos++] = _str[i];
					i++;
				}
				_size -= len;
			}
			return *this;

		}

五.查找函数和比较函数

        //利用C语言字符串函数
        size_t find(char c){
			for (size_t i = 0; i < size(); i++){
				if (_str[i] == c){
					return i;
				}
			}
			return npos;
		}

		size_t find(char *s){
			//使用C语言字符串函数
			char *ret = strstr(_str, s);
			if (ret == NULL){
				return npos;
			}
			return ret - _str;

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

		}
		bool operator==(const string& s)const{
			int key = strcmp(_str, s._str);
			return key == 0;
		}
		bool operator<=(const string& s)const{
			return *this<s || *this == s;
		}
		bool operator>(const string& s)const{
			return !(*this <= s);

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

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

六.operator<<和operator>>操作符重载

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

	istream& operator>>(istream& in, string& s){

		while (1){
			//cin时istream类的对象,get时istream类成员函数
			char ch = in.get();
			if (ch == ' ' || ch == '\\n'){
				break;
			}
            //+=里会开辟空间
			s += ch;
		}
		return in;

	}

具体细节看下面完整代码,还增加了迭代器的实现

七.完整代码

#include<iostream>
#include<assert.h>
#include<string>
#include<Windows.h>
#pragma warning(disable:4996)
using namespace std;

namespace my{
	class string{
		friend istream& operator>>(istream& in, string& s);
	public:
		typedef char* iterator;
		iterator begin(){
			return _str;
		}
		iterator end(){
			return _str + _size;
		}
		//要在堆上开辟空间将字符串拷贝过去,不能直接在初始化列表里初始化,否则不可修改
		//空字符串也要开辟一个空间,放\\0
		//拷贝构造函数
		string(const char *str="")
		:_str(new char[strlen(str)+1])
		{
			strcpy(_str, str);
			_size = strlen(str);
			_capacity = _size;
		}
		//拷贝构造函数
		string(const string& s){
			_str = new char[strlen(s._str) + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
			
		}
		//析构函数
		~string(){
			delete[] _str;
			_str = nullptr;
		}
		//赋值操作符
		string& operator=(const string& s){
			if (this != &s){
				char *newstr = new char[strlen(s._str) + 1];
				strcpy(newstr, s._str);
				delete[] _str;
				_str = newstr;
				_size = s._size;
				_capacity = s._capacity;
				
			}
			return *this;
		}

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

		size_t size()const{
			return _size;
		}
		size_t capacity()const{
			return _capacity;
		}
		const char *c_str(){
			return _str;
		}

		void push_back(char c){
			if (_size == _capacity){
				//细节 判断容量是否等于0
				size_t newcapacity = _capacity == 0 ? 2 : 2 * _capacity;
				//加1  预留\\0位置
				char *newstr = new char[newcapacity+1];
				strcpy(newstr, _str);
				delete[] _str;
				_str = newstr;
				_capacity = newcapacity;
			}
			_str[_size++] = c;
			//要在末尾加上\\0
			_str[_size] = '\\0';
			
		}

		void append(const char *s){
			size_t len = strlen(s) + _size;
			if (len > _capacity){
				char *newstr = new char[len + 1];
				strcpy(newstr, _str);
				delete[] _str;
				_str = newstr;
				_capacity = len;
			}
			strcpy(_str + _size, s);
			_size = len;
			
		}
		//服用push_back
		string& operator+=(char c){
			push_back(c);
			return *this;
		}
		//服用append
		string& operator+=(const char *s){
			append(s);
			return *this;
		}
		//不需要返回值,在函数体里this改变了
		void resize(size_t len, char c = '\\0'){
			if (len > _size){
				if (len > _capacity){
					char *temp = new char[len + 1];
					strcpy(temp, _str);
					delete[] _str;
					_str = temp;
					_capacity = len;
				}
				//用memset函数
				memset(_str + _size, c, len - _size);

			}
			_size = len;
			_str[_size] = '\\0';
		}
		void reserve(size_t newcapacity){
			//只有大于容量时才扩容
			if (newcapacity > _capacity){
				char *newstr = new char[newcapacity + 1];
				strcpy(newstr, _str);
				delete[] _str;
				_str = newstr;
				_capacity = newcapacity;

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

				return *this;

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

		}
		//两种情况,删除长度加上位置小于_size,删除长度加上位置大于等于_size。
		string& erase(size_t pos, size_t len = npos){
			assert(pos < _size);
			//用减比较好,防止越界,加的话npos最大值,加上一值容易越界
			if (len >= _size - pos){
				_str[pos] = '\\0';
				_size = pos;
			}
			else{
				size_t i = pos + len;
				while (i <= _size){
					_str[pos++] = _str[i];
					i++;
				}
				_size -= len;
			}
			return *this;

		}

		size_t find(char c){
			for (size_t i = 0; i < size(); i++){
				if (_str[i] == c){
					return i;
				}
			}
			return npos;
		}

		size_t find(char *s){
			//使用C语言字符串函数
			char *ret = strstr(_str, s);
			if (ret == NULL){
				return npos;
			}
			return ret - _str;

		}

		bool operator<(const string& s)const{
			int key = strcmp(_str, s._str);
			return key < 0;

		}
		bool operator==(const string& s)const{
			int key = strcmp(_str, s._str);
			return key == 0;
		}
		bool operator<=(const string& s)const{
			return *this<s || *this == s;
		}
		bool operator>(const string& s)const{
			return !(*this <= s);

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

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




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

		static size_t npos;
	};

	size_t string::npos = -1;

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

	istream& operator>>(istream& in, string& s){

		while (1){
			//cin时istream类的对象,get时istream类成员函数
			char ch = in.get();
			if (ch == ' ' || ch == '\\n'){
				break;
			}
			s += ch;
		}
		return in;

	}

	
}

以上是关于带你深入了解string类接口(传统写法实现string类)的主要内容,如果未能解决你的问题,请参考以下文章

C++初阶:STL —— stringstring类 | 浅拷贝和深拷贝(传统写法和现代写法) | string类的模拟实现

C++初阶:STL —— stringstring类 | 浅拷贝和深拷贝(传统写法和现代写法) | string类的模拟实现

C++string类

C++ String 深拷贝(传统写法+现代写法)

C语言面试题C++中String类浅拷贝,深拷贝的传统写法与现代写法

string类实现