STL篇一:string

文章目录

  • 前言
  • 1. STL的简单理解
    • 1.1 什么是STL
    • 1.2 STL的版本
    • 1.3 STL的六大组件
    • 1.4 STL的重要性
    • 1.5 STL的缺陷
  • 2. string类
    • 2.1 为什么学习string类?
      • 2.1.1 C语言中的字符串
      • 2.1.2 两个面试题
    • 2.2 标准库中的string类
      • 2.2.1 string类(了解)
      • 2.2.2 string类的常用接口说明
    • 2.3 string类的模拟实现中的一些问题
      • 2.3.1 经典的string类问题
      • 2.3.2 浅拷贝
      • 2.3.3 深拷贝
    • 2.4 string的模拟实现
      • 2.4.1 构造函数
      • 2.4.2 拷贝构造
      • 2.4.3 析构函数
      • 2.4.4 赋值运算符重载
      • 2.4.5 [ ]的实现
      • 2.4.6 扩容
      • 2.4.7 尾插
      • 2.4.8 插入一个字符串
      • 2.4.9 += 的运算符重载
      • 2.4.10 插入
      • 2.4.11 删除
      • 2.4.12 重新设置大小
      • 2.4.13 << 与 >> 的重载
      • 2.4.14 < 、>、==运算符重载
      • 2.4.15 查找函数
  • 3.全部代码
    • 3.1 String.h
    • 3.2 String.cpp
    • 3.3 Test.cpp
  • 4. 总结

前言

  可能很多人都不太清楚STL是什么,它是用来干什么的,大家在看完这篇文章之后相信能有个大概的了解,目前我只能说STL是C++的中十分重要的部分之一,是大家学习C++必须掌握的部分。

1. STL的简单理解

1.1 什么是STL

   STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。

1.2 STL的版本

  • 原始版本

Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本–所有STL实现版本的始祖。

  • P. J. 版本

由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。

  • RW版本

由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。

  • SGI版本

由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版本。被GCC(Linux)采用,可移植性好, 可公开、修改甚至贩卖,从命名风格和编程风格上看,阅读性非常高。我们后面学习STL要阅读部分源代码, 主要参考的就是这个版本。

1.3 STL的六大组件

在这里插入图片描述

1.4 STL的重要性

  1. 在笔试中

1. 二叉树层序打印
2. 重建二叉树
3. 两个栈实现一个队列

  1. 在面试中
    在这里插入图片描述
    3.在工作中

网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。

1.5 STL的缺陷

  1. STL库的更新太慢了。这个得严重吐槽,上一版靠谱是C++98,中间的C++03基本一些修订。C++11出来已经相隔了13年,STL才进一步更新。
  2. STL现在都没有支持线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的。
  3. STL极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取。
  4. STL的使用会有代码膨胀的问题,比如使用vector/vector/vector这样会生成多份代码,当然这是模板语法本身导致的。

2. string类

  string类是一个容器,它是我们学习STL的第一部分,我们的主线是讲解STL的容器,其余部分会在讲解容器时进行穿插讲解。

2.1 为什么学习string类?

2.1.1 C语言中的字符串

  C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问.

2.1.2 两个面试题

  1. 字符串转整形数字
  2. 字符串相加
  在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。

2.2 标准库中的string类

2.2.1 string类(了解)

  string类的文档介绍

  1. 字符串是表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_trai ts和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

  1. string是表示字符串的字符串类
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
  4. 不能操作多字节或者变长字符的序列。

  在使用string类时,必须包含#include头文件以及using namespace std;

2.2.2 string类的常用接口说明

在这里插入图片描述
  上面就是关于string修改部分的一些接口,比如插入、删除等等。
在这里插入图片描述
  上面就是关于string容量部分的一些接口,比如计算长度、计算容量等等。
  此外还有大量的其他接口,再后面我会将用的比较多的接口进行模拟实现,如果有小伙伴对其他接口也十分感兴趣,可以通过下面的链接进行跳转观看string的全部接口,对于每一个接口点进去都会有详细的文档说明和测试用例,十分便于大家理解。

2.3 string类的模拟实现中的一些问题

2.3.1 经典的string类问题

  上面已经对string类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。大家看下以下string类的实现是否有问题?

// 为了和标准库区分,此处使用String
class String
{
public:
	/*String()
	:_str(new char[1])
	{*_str = '\0';}
	*/
	//String(const char* str = "\0") 错误示范
	//String(const char* str = nullptr) 错误示范
	String(const char* str = "")
	{
		// 构造String类对象时,如果传递nullptr指针,可以认为程序非
		if (nullptr == str)
		{
			assert(false);
			return;
		}
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	~String()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};
// 测试
void TestString()
{
	String s1("hello bit!!!");
	String s2(s1);
}

在这里插入图片描述

  我们会发现程序运行崩溃,这是因为上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。这个知识点我在C++类和对象中的赋值运算符部分进行过详细的讲解,有不懂的小伙伴可以点击链接进行跳转阅读。

2.3.2 浅拷贝

  浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
  可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。

2.3.3 深拷贝

  如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
在这里插入图片描述
  深拷贝的具体如何实现在下一小节模拟实现中进行讲解。

2.4 string的模拟实现

2.4.1 构造函数

//声明,这一行是放在String.h文件中的
//string(const char* str = "");

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

  在实现拷贝构造时一般都会采用缺省值的方式,是为了避免使用者在实例化对象时忘记赋予初始值。对于缺省参数,在声明和定义中,一般是在声明时书写缺省值。并且在开辟空间时是要开辟容量+1 个空间,因为参数的字符串中是包含一个字符零的,因此在开辟时要给字符零预留一个空间,

2.4.2 拷贝构造

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

  拷贝构造一定需要注意的点是参数必须进行引用传参,负责会出现无限递归的问题,具体过程在C++类和对象有关拷贝构造的小节有详细说明。
  初次之外这里也是涉及到了深拷贝,在拷贝构造中,我们是重新开辟了一块新空间进行拷贝,因此原来的对象与新对象指向的是不同的空间,在析构时是不会发生对一块空间析构两次的情况。

2.4.3 析构函数

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

  就是释放空间,并将对象中的成员变量都进行归零。需要注意的是在释放空间是要加 [ ]中括号,对于单个变量是不需要加中括号的,但是对于多个变量,比如数组在释放时需要加中括号。详细原理见C/C++动态内存管理。

2.4.4 赋值运算符重载

WY::string& WY::string::operator = (const string& s)
{
	if (this != &s)
	{
		char* tmp = new char[s._capacity + 1];
		strcpy(tmp, s._str);
		delete[] _str;

		_str = tmp;
		_capacity = s._capacity;
		_size = s._size;
	}
	return *this;
}

  首先需要说明的是,这里的引用传参仅仅只是为了提高效率,因为在实现了拷贝构造函数之后再进行传值传参就不会发生无穷递归了。为了避免浅拷贝问题,需要重新开辟一块空间,将数据进行拷贝过去,然后将被赋值的对象的原数据进行释放,将新开辟的空间给了被赋值对象,再将其余成员变量依次进行修改。

2.4.5 [ ]的实现

char& WY::string::operator[](size_t index)
{
	return _str[index];
}

const char& WY::string::operator[](size_t index)const
{
	return _str[index];
}

  中括号的实现可以让用户更加方便的查询数据,但是const成员是无法调用非const 的成员函数的,因此要将const成员函数也要实现一下。

2.4.6 扩容

//扩容
void WY::string::reserve(size_t n)
{
	if (_capacity < n)
	{
		_capacity = n;
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;

		_str = tmp;
	}
}

  重新开辟一块空间再将原数据进行拷贝就可以了,要记得释放原对象的空间噢!!

2.4.7 尾插

void WY::string::push_back(char c)
{
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	_str[_size] = c;
	_size++;
	_str[_size] = '\0';
}

  尾插无法避免的情况就是当容量已满时就需要进行扩容。

2.4.8 插入一个字符串

void WY::string::append(const char* str)
{
	int len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}

	strcpy(_str + _size, str);
	_size +=len;
}

  上面的尾插只是插入了一个字符,这个函数是用来专门插入一个字符串的。在库函数中这个函数是可以实现在各个位置进行插入,这里我只实现了尾部插入,需要更加详细了解的可以去看相关的需求文档。

2.4.9 += 的运算符重载

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

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

  这里的逻辑跟尾插一样,直接调用尾插就好了,这也是进行分模块实现功能的好处,可以进行代码复用,十分方便。

2.4.10 插入

void WY::string::insert(size_t pos, char c)
{
	assert(pos >= 0);
	if (_size == _capacity)
	{
		reserve(_capacity * 2);
	}

	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		end--;
	}
	_str[pos] = c;
	_size++;
}

  只要插入数据就需要进行扩容判断,然后从后往前不断挪动数据,一直到pos位置再进行插入,这一块的时间复杂度是O(n)。

void WY::string::insert(size_t pos, const char* str)
{
	assert(pos >= 0);
	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;
}

  插入一个字符串和插入一个字符的逻辑是一样的。

2.4.11 删除

void WY::string::erase(size_t pos, size_t len)
{
	assert(pos < _size);
	if (len == npos ||pos + len > _size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		size_t begin = pos + len;
		while (begin <= _size)
		{
			_str[pos++] = _str[begin++];
		}
		_size -= len;
	}
}

  删除有两种情况,一种是将pos位置之后的数据全部删除,一种是只删除pos位置之后长度为len的数据。这里的npos是一个全局变量,是为了与库函数中的实现方式相对应,同时它也是参数len的缺省值。
  如果用户没有具体说明删除多长的数据,默认为删除pos位置之后的所有数据,而当你删除的数据长度加上pos位置大于整个数据的大小说明此时也是删除了pos位置之后的所有数据。另一种情况就是将需要删除的数据后面所剩的数据全部挪移到前面即可。

2.4.12 重新设置大小

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

  如果小于原数据大小,直接在n位置填上’\0’即可,如果大于原数据,就需要先扩容,再多余的空间上填上字符c,这里的c的默认参数是’\0’。

2.4.13 << 与 >> 的重载

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

istream& WY::operator>>(istream& _cin, WY::string& s)
{
	char ch = _cin.get();
	while (ch != ' ' && ch != '\n')
	{
		s += ch;
		ch = _cin.get();
	}
	return _cin;
}

  ostream与istream也是一个对象,一个是输出流(cout),一个是输入流对象(cin)。这个运算符重载需要注意的是这个是写在类外面的,并不是一个类的成员函数,因为如果写成成员函数的话,就是这个样子operator>>(this,istream& _cin),由于this是在前面,因此在外面调用的话就需要书写成 s << cout,而我们正常写的是cout << s,所有需要在类的外面进行声明定义。但是类外面的函数要是无法访问类内部的成员变量的,所以还需要将这两个函数声明为该类的友元函数,有关友元函数的讲解在C++类和对象中有详细解释。当一个函数被声明为友元函数时,这个函数就可以访问该类的成员变量。
  还需要注意的一个点是这两个函数一般都是引用返回,一是为了提高效率(引用传参、引用返回都不需要再进行拷贝构造,因此可以提高效率),二是可以实现连续输入输出。比如:cout << s << s1 ------>operator << (opeartor <<(cout,s),s1)

2.4.14 < 、>、==运算符重载

bool WY::string::operator<(const string& s)
{
	return strcmp(_str, s._str) < 0;
}

bool WY::string::operator==(const string& s)
{
	return strcmp(_str, s._str) == 0;
}

bool WY::string::operator<=(const string& s)
{
	return (*this < s) || (*this == s);
}

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

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

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

  实现上两个其他的就可以直接进行复用了。

2.4.15 查找函数

size_t WY::string::find(char c, size_t pos) const
{
	for (size_t i = pos; i < size(); i++)
	{
		if (_str[i] == c)
			return i;
	}
	
	return npos;
}

size_t WY::string::find(const char* s, size_t pos) const
{
	const char* p = strstr(_str + pos, s);
	if (p)
	{
		return p - _str;
	}
	else
	{
		return npos;
	}
}

  pos是用户可以指定从哪里开始进行查找,查找单个字符依次便利尽心比较即可。在查找子串时,可以调用库函数的strstr函数,如果找到它返回的是子串的首地址,如果没找到返回的是空指针。还需要注意的是指针减指针的含义是两个指针之间的数据个数,这里也就是子串首字母的位置

3.全部代码

3.1 String.h

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace WY
{
    class string
    {
        friend ostream& operator<<(ostream& _cout, const WY::string& s);
        friend istream& operator>>(istream& _cin, WY::string& s);

    public:
        typedef char* iterator;
        typedef const char* const_iterator;

    public:
        string(const char* str = "");

        string(const string& s);

        string& operator = (const string& s);

        ~string();
            //
            // iterator
        iterator begin();
        const_iterator begin()const;

        iterator end();

        const_iterator end()const;
            /

            // modify

        void push_back(char c);

        string& operator+=(char c);

        void append(const char* str);

        string& operator+=(const char* str);

        void clear();

        void swap(string& s);

        const char* c_str()const;



        /

        // capacity

        size_t size()const;

        size_t capacity()const;

        bool empty()const;

        void resize(size_t n, char c = '\0');

        void reserve(size_t n);



        /

        // access

        char& operator[](size_t index);

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



        /

        //relational operators

        bool operator<(const string& s);

        bool operator<=(const string& s);

        bool operator>(const string& s);

        bool operator>=(const string& s);

        bool operator==(const string& s);

        bool operator!=(const string& s);



        // 返回c在string中第一次出现的位置

        size_t find(char c, size_t pos = 0) const;

        // 返回子串s在string中第一次出现的位置

        size_t find(const char* s, size_t pos = 0) const;

        // 在pos位置上插入字符c/字符串str,并返回该字符的位置

        void insert(size_t pos, char c);

        void insert(size_t pos, const char* str);

        // 删除pos位置上的元素,并返回该元素的下一个位置
        void erase(size_t pos, size_t len = npos);

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

        const static size_t npos;
    };

    ostream& operator<<(ostream& _cout, const WY::string& s);
    istream& operator>>(istream& _cin, WY::string& s);

    void test_string1();
    void test_string2();
    void test_string3();
    void test_string4();
    void test_string5();
    void test_string6();
};

3.2 String.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"String.h"

const size_t WY::string::npos = -1;


//构造
WY::string::string(const char* str)
	:_size(strlen(str))
	,_capacity(strlen(str))
{
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

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

WY::string& WY::string::operator = (const string& s)
{
	if (this != &s)
	{
		char* tmp = new char[s._capacity + 1 ];
		strcpy(tmp, s._str);
		delete[] _str;

		_str = tmp;
		_capacity = s._capacity;
		_size = s._size;
	}
	return *this;
}

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

char& WY::string::operator[](size_t index)
{
	return _str[index];
}

const char& WY::string::operator[](size_t index)const
{
	return _str[index];
}

//扩容
void WY::string::reserve(size_t n)
{
	if (_capacity < n)
	{
		_capacity = n;
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;

		_str = tmp;
	}
}

void WY::string::push_back(char c)
{
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	_str[_size] = c;
	_size++;
	_str[_size] = '\0';
}

void WY::string::append(const char* str)
{
	int len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}

	strcpy(_str + _size, str);
	_size +=len;
}

const char* WY::string::c_str()const
{
	return _str;
}

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

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

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

void WY::string::swap(string& s)
{
	char* tmp = new char[_capacity + 1];
	strcpy(tmp, _str);
	strcpy(_str, s._str);
	strcpy(s._str, tmp);
}

size_t WY::string::size()const
{
	return _size;
}

size_t WY::string::capacity()const
{
	return _capacity;
}

bool WY::string::empty()const
{
	return _size == 0;
}

void WY::string::insert(size_t pos, char c)
{
	assert(pos >= 0);
	if (_size == _capacity)
	{
		reserve(_capacity * 2);
	}

	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		end--;
	}
	_str[pos] = c;
	_size++;
}

void WY::string::insert(size_t pos, const char* str)
{
	assert(pos >= 0);
	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;
}


void WY::string::erase(size_t pos, size_t len)
{
	assert(pos < _size);
	if (len == npos ||pos + len > _size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		size_t begin = pos + len;
		while (begin <= _size)
		{
			_str[pos++] = _str[begin++];
		}
		_size -= len;
	}
}

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

WY::string::iterator WY::string::begin()
{
	return _str;
}

WY::string::const_iterator WY::string::begin()const
{
	return _str;
}

WY::string::iterator WY::string::end()
{
	return _str + _size;
}

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

// cout << s ----> operator <<(cout,s) 
ostream& WY::operator<<(ostream& _cout, const WY::string& s)
{
	for (size_t i = 0; i < s.size(); i++)
	{
		_cout << s[i];
	}
	return _cout;
}


istream& WY::operator>>(istream& _cin, WY::string& s)
{
	char ch = _cin.get();
	while (ch != ' ' && ch != '\n')
	{
		s += ch;
		ch = _cin.get();
	}
	return _cin;
}

bool WY::string::operator<(const string& s)
{
	return strcmp(_str, s._str) < 0;
}

bool WY::string::operator==(const string& s)
{
	return strcmp(_str, s._str) == 0;
}

bool WY::string::operator<=(const string& s)
{
	return (*this < s) || (*this == s);
}

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

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

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

size_t WY::string::find(char c, size_t pos) const
{
	for (size_t i = pos; i < size(); i++)
	{
		if (_str[i] == c)
			return i;
	}
	
	return npos;
}

size_t WY::string::find(const char* s, size_t pos) const
{
	const char* p = strstr(_str + pos, s);
	if (p)
	{
		return p - _str;
	}
	else
	{
		return npos;
	}
}

3.3 Test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"String.h"


void WY::test_string1()
{
    string s("hello world");
    cout << s.c_str() << endl;

    string s1(s);
    cout << s1.c_str() << endl;

    string s2;
    s2 = s;
    cout << s2.c_str() << endl;

    s.push_back('x');
    cout << s.c_str() << endl;

    s1.append("xxx");
    cout << s1.c_str() << endl;
    
    s2 += 'x';
    cout << s2.c_str() << endl;

    s2 += "xxx";
    cout << s2.c_str() << endl;

}

void WY::test_string2()
{
    string s("hello world");
    s.insert(0, 'x');
    cout << s.c_str() << endl;

    string s1("hello world");
    s1.insert(0, "xxx");
    cout << s1.c_str() << endl;

    s.erase(0, 1);
    cout << s.c_str() << endl;

    s1.erase(0, 3);
    cout << s1.c_str() << endl;

    s1.erase(1);
    cout << s1.c_str() << endl;

}

void WY::test_string3()
{
    string s("hello world");
    s.resize(5);
    cout << s.c_str() << endl;

    s.resize(20,'x');
    cout << s.c_str() << endl;
}

void WY::test_string4()
{
    string s;
    cin >> s;
    cout << s << endl;
}

void WY::test_string5()
{
    string s("hello world");

    string s1("hello world");
    cout << (s == s1) << endl;

    string s2("hello");
    cout << (s == s2) << endl;

    string s3("a");
    string s4("b");
    cout << (s3 < s4) << endl;
}

void WY::test_string6()
{
    string s("hello world");
    size_t pos = s.find('w');
    cout << pos << endl;
}

int main()
{
	WY::test_string1();
    //WY::test_string2();
    //WY::test_string3();
    //WY::test_string4();
    //WY::test_string5();
    //WY::test_string6();
	return 0;
}

4. 总结

  STL是学习是C++中必不可少的一部分,必须熟练掌握才行,我建议大家可以通过刷题来快速掌握STL的使用,熟练使用是最重要的,其次才是了解它的底层实现。
  如果大家发现有什么错误的地方,可以私信或者评论区指出喔。我会继续深入学习C++,希望能与大家共同进步,那么本期就到此结束,让我们下期再见!!觉得不错可以点个赞以示鼓励!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/317677.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL性能优化】- MySQL结构与SQL执行过程

MySQL结构与SQL执行过程 &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f3c6; 博客首页 怒放吧德德 To记录领地 &#x1f31d;分享学习心得&#xff0c;欢迎指正…

Mysql事务隔离级别是怎么实现的?

Mysql事务 事务概念事务特性事务并发事务隔离级别MVCC多版本并发控制 事务概念 小钢同学今天发工资了&#xff0c;赶紧打开招商银行app看看工资到账了没有&#xff0c;查看余额300 嗯&#xff0c;今天心情好&#xff0c;给对象转账50大元买lv包包去&#xff0c;最后的结果肯定…

设计模式之组合模式【结构型模式】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档> 学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某…

Validation--自定义校验

前言&#xff1a; 今天学到这个&#xff0c;闲着也是闲着&#xff0c;就写一个记录一下&#xff0c;也算是总结 我们的步骤是这样的 1.自定义注解State 2.自定义校验数据的类StateValidation实现ConstrainValidator接口 3.在需要校验的地方使用自定义注解 1.自定义注解 这…

Kafka的核心原理

Topic的分区和副本机制 分区有什么用呢? 作用&#xff1a; 1- 避免单台服务器容量的限制: 每台服务器的磁盘存储空间是有上限。Topic分成多个Partition分区&#xff0c;可以避免单个Partition的数据大小过大&#xff0c;导致服务器无法存储。利用多台服务器的存储能力&#…

【学习心得】Git深入学习

一、深入学习Git必须熟悉两个概念 &#xff08;1&#xff09;【四个区】Git本地有三个区&#xff0c;远程仓库也可以看出成一个区域 工作区、暂存区、本地仓库、远程仓库。 通过四句话来充分理解这三个区 第一句话&#xff1a;你创建的一个文件夹&#xff0c;并且将它初始化…

Nocalhost 为 KubeSphere 提供更强大的云原生开发环境

1 应用商店安装 Nocalhost Server 已集成在 KubeSphere 应用商店&#xff0c;直接访问&#xff1a; 设置应用「名称」&#xff0c;确认应用「版本」和部署「位置」&#xff0c;点击「下一步」&#xff1a; 在「应用设置」标签页&#xff0c;可手动编辑清单文件或直接点击「安装…

Linux:信号

目录 1.信号 2.信号的过程 a.信号的产生 1:键盘产生, 异常产生 2:系统调用产生信号 3.软件条件产生信号 4.硬件异常产生信号 b.信号的发送 c.信号的处理 d.总结与思考 3.信号保存 1.信号及其它相关常见概念 2.在内核中的表示 3.sigset_t 4. 信号集操作函数 4.信…

MySQL 管理端口

错误 客户出现 MySQL连接数 超过 最大连接数的现象 ERROR 1040 (HY000): Too many connections 出现该现象&#xff0c;一般的解决方法&#xff1a; 1.修改配置文件中的最大连接数&#xff0c;之后重启数据库 2.如果配置文件中没有设置 连接超时时间的参数。8小时后&#…

前端 TS 语法 接口(2)

介绍 TypeScript的核心原则之一是对值所具有的shape进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里&#xff0c;接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。 只读属性 readonly 一些对象属性只能在对象刚刚创建的…

iOS开发进阶(六):Xcode14 使用信号量造成线程优先级反转问题修复

文章目录 一、前言二、关于线程优先级反转三、优先级反转会造成什么后果四、怎么避免线程优先级反转五、使用信号量可能会造成线程优先级反转&#xff0c;且无法避免六、延伸阅读&#xff1a;iOS | Xcode中快速打开终端6.1 .sh绑定6.2 执行 pod install 脚本 七、延伸阅读&…

MySQL性能测试及调优中的死锁处理方法

以下从死锁检测、死锁避免、死锁解决3个方面来探讨如何对MySQL死锁问题进行性能调优。 死锁检测 通过SQL语句查询锁表相关信息&#xff1a; &#xff08;1&#xff09;查询表打开情况 SHOW OPEN TABLES WHERE IN_USE> 01 &#xff08;2&#xff09;查询锁情况列表 SEL…

达梦数据实时同步软件DMHS介绍和原理

1、产品介绍 达梦数据实时同步软件&#xff08;以下简称 DMHS&#xff09;是支持异构环境的高性能、高可靠、高可扩展数据库实时同步复制系统。该产品采用基于日志的结构化数据复制技术&#xff0c;不依赖主机上源数据库的触发器或者规则&#xff0c;对主机源数据库系统几乎无影…

计算机msvcp140.dll丢失如何解决,分享3个简单有效的方法

在计算机系统运行过程中&#xff0c;用户有时会遇到一个常见的错误提示——msvcp140.dll文件缺失&#xff0c;这一问题的发生往往会导致部分软件无法正常启动或运行。“针对计算机系统中出现的msvcp140.dll缺失问题&#xff0c;小编将详尽阐述并探讨5种有效的解决策略。每一种方…

Linux的SSH服务

一.SSH服务简介 1.什么是SSH SSH&#xff08;Secure Shell&#xff09;是一种安全通道协议&#xff0c;主要用来实现字符界面的远程登录、远程复制等功能。SSH 协议对通信双方的数据传输进行了加密处理&#xff0c;其中包括用户登录时输入的用户口令&#xff0c;SSH 为建立在应…

java大学生宿舍共享厨房系统宿舍自习室宿舍洗衣房系统源码包含技术文档

主要功能&#xff1a;学生可注册登录&#xff0c;预约自己宿舍楼栋的共享厨房和评价&#xff0c;也可以使用该楼栋的洗衣房&#xff0c;查看洗衣机吹风机的使用情况和报修&#xff0c;还可以进入该楼栋自习室打卡和评价。管理员可管理所有的学生和宿管&#xff0c;分配宿舍&…

【期末不挂科-C++考前速过系列P4】大二C++实验作业-继承和派生(3道代码题)【解析,注释】

前言 大家好吖&#xff0c;欢迎来到 YY 滴C考前速过系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《…

【MATLAB源码-第110期】基于matlab的哈里斯鹰优化算发(HHO)无人机三维路径规划,输出做短路径图和适应度曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 哈里斯鹰优化算法&#xff08;Harris Hawk Optimization, HHO&#xff09;是一种受自然界捕食行为启发的优化算法。它基于哈里斯鹰的捕猎策略和行为模式&#xff0c;主要用于解决各种复杂的优化问题。这个算法的核心特征在于…

神经辐射场(NeRFs)的研究进展

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 摘要Abstract文献阅读&#xff1a;神经辐射场&#xff08;NeRFs&#xff09;的研究进展1、研究背景2、方法发展3、相关方法3.1、Pixel NeRF3.2、RegNeRF3.3、Mip-Ne…

全链路追踪关键技术-TraceId、SpanId生成规则

链路追踪的traceid原理梳理 如何追踪微服务调用&#xff1f; ● traceId&#xff0c;用于标识某一次具体的请求ID。当用户的请求进入系统后&#xff0c;会在RPC调用网络的第一层生成一个全局唯一的traceId&#xff0c;并且会随着每一层的RPC调用&#xff0c;不断往后传递&…