从零开始:C++ String类的模拟实现

慈云数据 2024-05-30 技术支持 32 0

文章目录

  • 引言
  • 1.类的基本结构
  • 2.构造函数和析构函数
  • 3.基本成员函数
  • 总结

    在这里插入图片描述

    引言

    在C++编程中,字符串操作是非常常见且重要的任务。标准库中的std::string类提供了丰富且强大的功能,使得字符串处理变得相对简单。然而,对于学习C++的开发者来说,深入理解std::string的内部实现原理是非常有益的。通过亲手实现一个类似的String类,不仅可以帮助我们掌握面向对象编程的基本概念,还能增强我们对内存管理和字符串操作的理解。

    在这篇博客中,我们将从零开始,逐步实现一个自定义的C++ String类。我们的目标是构建一个功能完整且高效的字符串类,同时尽可能地模仿std::string的行为。我们将讨论类的基本结构、构造函数和析构函数的实现、基本成员函数的设计、运算符重载、内存管理,以及如何编写测试代码来验证我们的实现。

    通过这篇文章,您将学到如何在C++中进行动态内存分配和管理,如何实现深拷贝和移动语义,如何重载运算符以提升类的易用性,等等。无论您是刚刚入门的C++学习者,还是希望深入理解C++底层实现的开发者,这篇文章都将为您提供宝贵的知识和实践经验。

    让我们一起来探索C++ String类的实现之旅吧!

    1.类的基本结构

    1.1定义类

    #include
    #include
    using namespace std;
    namespace lyrics
    {
    	class string
    	{
    	public:
    		typedef char* iterator;
    		typedef const char* const_iterator;
    		//迭代器
    		iterator begin();
    		iterator end();
    		const_iterator begin()const;
    		const_iterator end()const;
    		//构造函数
    		string(const char* str = "");
    		string(const string& s);
    		//析构函数
    		~string();
    		//
    		const char* c_str() const;
    		//返回大小
    		size_t size() const;
    		//运算符重载
    		char& operator[](size_t pos);
    		const char& operator[](size_t pos)const;
    		//空间扩容
    		void reserve(size_t n);
    		//尾插一个字符
    		void push_back(char ch);
    		//尾插一个字符串
    		void append(const char* str);
    		//运算符重载+=操作
    		string& operator+=(char ch);
    		string& operator+=(const char* str);
    		//插入操作,插入一个字符串和插入一个字符
    		void insert(size_t pos, char ch);
    		void insert(size_t pos, const char* str);
    		//删除某段字符
    		void erase(size_t = 0, size_t len = npos);
    		//查找某个字符串或者字符
    		size_t find(char ch, size_t pos = 0);
    		size_t find(const char* str, size_t pos = 0);
    		//赋值拷贝
    		string& operator=(const string& s);
    		//交换函数
    		void swap(string& s);
    		//取子串
    		string substr(size_t pos = 0, size_t = npos);
    		//比较函数运算符重载
    		bool operator=(const string& s)const;
    		bool operator==(const string& s)const;
    		//清理
    		void clear();
    	private:
    		size_t _size;
    		size_t _capacity;
    		char* _str;
    		const static size_t npos;
    	};
    	//非成员函数,,重载流插入和流提取
    	istream& operator>>(istream& is, string& str);
    	ostream& operator
    	_str = new char[_size + 1];
    	_capacity = _size;
    	strcpy(_str, str);
    }
    
    	char* tmp = new char[s._capacity + 1];
    	strcpy(tmp, s._str);
    	delete[] _str;
    	_str = tmp;
    	_size = s._size;
    	_capacity = s._capacity;
    	return *this;
    }
    
    	return _str;
    }
    
    	delete[] _str;
    	_str = nullptr;
    	_size = 0;
    	_capacity = 0;
    }
    
    	return _size;
    }
    
    	assert(pos  
    

    4.0删除某段字符串

    注意:在声明中len的缺省参数给的是npos,当我的长度大于pos对应的后面对应的长度的时候,这时候就有多少删多少,所以我们需要判断一下,第一个if判断的就是判断我们删除的长度是否已经超过了后面的长度,如果超过了就直接进入第一个if删除后面的所有,也就是把pos位置置为\0,然后将_size更新,如果不是的话可以直接将pos+len位置的子字符串拷贝到pos位置之后

    //从pos位置删除len个字符
    void string::erase(size_t pos, size_t len)
    {
    	assert(pos = _size - pos)
    	{
    		_str[pos] = '\0';
    		_size = pos;
    	}
    	else
    	{
    		strcpy(_str + pos, _str + pos + len);//直接把后面的copy到前面
    		_size -= len;
    	}
    }
    

    4.1查找函数

    • 查找单个字符
      size_t string::find(char ch, size_t pos)
      {
      	for (size_t i = pos;i  
      
      • 查找字符串

        查找字符串的话可以直接用C语言的库函数进行查找

        size_t string::find(const char* sub, size_t pos)
        {
        	const char* str = strstr(_str + pos, sub);
        	return str - _str;
        }
        

        4.2深拷贝

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

        4.3交换函数

        这里不用库里的交换函数因为库里的交换函数的效率太低了,我们可以简单看看库里交换函数的代码

        在这里插入图片描述

        这里可以看到库里的swap函数是直接拷贝构造一个零时的对象,然后进行两次赋值拷贝,这样做效率是极低的,因为是内置类型,两次赋值拷贝都会进行创建新空间,然后释放旧的空间,这样的成本是很大的,所以可以直接写一个swap对内置类型进行交换,直接交换两个指针的指向,还有size和capacity即可

        void string::swap(string& s)
        {
        	//内置类型交换代价更小
        	std::swap(_str, s._str);
        	std::swap(_size, s._size);
        	std::swap(_capacity, s._capacity);
        }
        

        4.4取子串

        string string::substr(size_t pos, size_t len)
        {
        	//检查pos是否合法
        	assert(pos  _size - pos)
        	{
        		//直接取后面的子串
        		string sub(_str + pos);//从pos位置开始进行拷贝构造!!!!
        		//返回子串
        		return sub;
        	}
        	else
        	{
        		//构造子串
        		string sub;
        		//预开辟空间
        		sub.reserve(len);
        		//循环拷贝
        		for (size_t i = 0;i  
        

        4.5比较函数operator的一系列重载

        这里只需要重载两个即可,其他的只需要进行复用就够了,比较函数的重载可以直接调用C语言中的字符串比较函数

        bool string::operator
        	return strcmp(_str, s._str) (istream& is, string& str)
        {
        	str.clear();
        	char ch = is.get();
        	while (ch != ' '&& ch != '\n')
        	{
        		str += ch;
        	}
        	return is;
        }
        
        • 流提取

          流提取也不用直接访问成员变量,流提取可以直接一个字符一个字符的访问,通过operator[]的重载访问,一个一个大打印

          ostream& operator
          	for (size_t i = 0;i 
微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon