C++string类(个人笔记)


1.认识string的接口以及熟练使用常用接口

1.1string类对象的常见构造

constructor函数名称功能说明
string() (重点)构造空的string类对象,即空字符串
string(const char* s) (重点)用C-string来构造string类对象
string(size_t n, char c)string类对象中包含n个字符c
string(const string&s) (重点)拷贝构造函数

演示:

void Teststring()
{
 	string s1; // 构造空的string类对象s1
 	string s2("hello bit"); // 用C格式字符串构造string类对象s2
 	string s3(s2); // 拷贝构造s3
}

1.2string类对象的容量操作

函数名称功能说明
size(重点)返回字符串有效字符长度
length返回字符串有效字符长度
capacity (重点)返回空间总大小
empty(重点)检测字符串释放为空串,是返回true,否则返回false
clear(重点)清空有效字符
reserve(重点)为字符串预留空间
resize(重点)将有效字符的个数该成n个,多出的空间用字符c填充

注意:

  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
  2. clear()只是将string中有效字符清空,不改变底层空间大小。
  3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(size_t n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
  4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。

演示:

#include<iostream>
using namespace std;
#include<string>
//
//测试string容量相关的接口
//size/clear/resize
void Teststring1()
{
	//注意:string类对象支持直接用cin和cout进行输入和输出
	string s("helle,ljh !");
	cout << s.size() << endl;
	cout << s.length() << endl;
	cout << s.capacity() << endl;
	cout << s << endl;

	//将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
	s.clear();
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	//将s中有效字符个数增加到10个,多出位置yon‘a’进行填充
	//"aaaaaaaaaa"
	s.resize(10, 'a');
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	//将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
	//“aaaaaaaaaa\0\0\0\0\0”
	//注意此时s中有效字符个数已经增加到15个
	s.resize(15);
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	cout << s << endl;

	//将s中的有效字符个数缩小到5个
	s.resize(5);
	cout << s.size() << endl;
	cout << s.capacity() << endl;
	cout << s << endl;
}
///
void Teststring2()
{
	string s;
	//测试reserve是否会改变string中有效元素个数
	s.reserve(100);
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	//测试reserve参数小于string的底层空间大小时,是否会将空间缩小
	s.reserve(50);
	cout << s.size() << endl;
	cout << s.capacity() << endl;
}

//
//利用reserve提高插入数组的效率,避免增容带来的开销
void TestPushBack()
{
	string s;
	size_t sz = s.capacity();
	cout << "making s grow:\n" << endl;
	for (int i = 0;i < 100;i++)
	{
		s.push_back('a');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed" << sz << endl;
		}
	}
}

//构建string时,如果提前已经知道string中大概要放多少个元素,可提前将string中空间设置好
void TestPushBackReserve()
{
	string s;
	s.reserve(100);
	size_t sz = s.capacity();

	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('a');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

1.3string类对象的访问及遍历操作

函数名称功能说明
operator[] (重点)返回pos位置的字符,const string类对象调用
begin+endbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin+rendrend获取一个字符的迭代器 + rbegin获取最后一个字符下一个位置的迭代器
范围forC++11支持更简洁的范围for的新遍历方式

演示:

//string的遍历
//begin()+end() for+[] 范围for
//注意:string遍历时使用最多的还是for+下标或者范围for(C++11后才支持)
//begin()+end()大多数使用在需要使用STL提供的算法操作string时,比如:采用reverse逆置string
void Teststring3()
{
	string s1("hello ljh");
	const string s2("Hello ljh");
	cout << s1 << " " << s2 << endl;
	cout << s1[0] << " " << s2[0] << endl;

	s1[0] = 'H';
	cout << s1 << endl;
	//s2[0]='h';代码编译失败,因为const类型对象不能修改
}

void Teststring4()
{
	string s("hello ljh");
	//3种遍历方式:
	// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
	// 另外以下三种方式对于string而言,第一种使用最多
	//1.for+operator[]
	for (size_t i = 0;i < s.size();i++)
	{
		cout << s[i] << endl;
	}
	//2.迭代器
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << endl;
		++it;
	}

	//string::reverse_iterator rit=s.rbegin();
	//C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
	auto rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit << endl;
	}
	//范围for
	for (auto ch : s)
	{
		cout << ch << endl;
	}
}

1.4string类对象的修改操作

函数名称功能说明
push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+=(重点)在字符串后追加字符串str
c_str返回C格式字符串
find+npos从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回

注意:

  1. 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
  2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

演示:

//测试string:
//1.插入(拼接)方式:push_back append aperator+=
//2.正向和反向查找:find()+rfind()
//3.截取字串:substr()
//4.删除:erase
void Teststring5()
{
	string str;
	str.push_back(' ');   // 在str后插入空格
	str.append("hello");  // 在str后追加一个字符"hello"
	str += 'b';           // 在str后追加一个字符'b'   
	str += "it";          // 在str后追加一个字符串"it"
	cout << str << endl;
	cout << str.c_str() << endl;   // 以C语言的方式打印字符串

	//获取file的后缀
	string file("string.cpp");
	size_t pos = file.rfind('.');
	string suffix(file.substr(pos, file.size() - pos));
	cout << suffix << endl;

	// npos是string里面的一个静态成员变量
	// static const size_t npos = -1;

	//取出url中的域名
	string url("http://www.cplusplus.com/reference/string/string/find/");
	cout << url << endl;
	size_t start = url.find("://");
	if (start == string::npos)
	{
		cout << "invalid url" << endl;
		return;
	}
	start += 3;
	size_t finish = url.find('/', start);
	string address = url.substr(start, finish - start);
	cout << address << endl;
	// 删除url的协议前缀
	pos = url.find("://");
	url.erase(0, pos + 3);
	cout << url << endl;
}

2.vs 和g++下string结构的说明

注意:下面结构是在32位平台下进行验证,32位平台下指针占4个字节.

  1. vs下string结构
    string总共占28个字节,先是有一个联合体,联合体用来定义string中字符串的存储空间:
    当字符串长度小于16时,使用内部固定的字符数组来存放
    当字符串长度大于等于16时,从堆上开辟空间
union _Bxty
{ 	// storage for small buffer or pointer to larger one
 	value_type _Buf[_BUF_SIZE];
 	pointer _Ptr;
 	char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
最后:还有一个指针做一些其他事情。
16+4+4+4=28字节
在这里插入图片描述

  1. g++下string的结构
    G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:
    空间总大小
    字符串有效长度
    引用计数
struct _Rep_base
{
 	size_type _M_length;
 	size_type _M_capacity;
 	_Atomic_word _M_refcount;
};

指向堆空间的指针,用来存储字符串。

3.string类运用的笔试题

仅反转字母
在这里插入图片描述

class Solution {
public:
    bool isLetter(char ch)
    {
        if('a'<=ch&&ch<='z')
        {
            return true;
        }
        if('A'<=ch&&ch<='Z')
        {
            return true;
        }
        return false;
    }

    void reverse(string& s)
    {
        size_t begin=0;
        size_t end=s.size()-1;
        while(begin<end)
        {
            if(isLetter(s[begin])&&isLetter(s[end]))
            {
                char temp=s[begin];
                s[begin]=s[end];
                s[end]=temp;
                begin++;
                end--;
            }
            else if (!isLetter(s[begin]))
            {
                begin++;
            }
            else if (!isLetter(s[end]))
            {
                end--;
            }
        }
    }


    string reverseOnlyLetters(string s)
    {
        if(s.empty())
        {
            return s;
        }
        reverse(s);
        return s;
    }
};

找字符串中第一个只出现一次的字符
在这里插入图片描述

class Solution {
public:
    int firstUniqChar(string s)
    {
        int count[256]={0};
        int size=s.size();
        for(int i=0;i<size;i++)
        {
            count[s[i]]++;
        }
        for(int i=0;i<size;i++)
        {
            if(count[s[i]]==1)
            {
                return i;
            }
        }
        return -1;
    }
};

字符串里面最后一个单词的长度
在这里插入图片描述

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string s;
    while(getline(cin,s))
    {
        size_t pos=s.rfind(' ');
        cout<<s.size()-pos-1<<endl;
    }
    return 0;
}

验证回文串
在这里插入图片描述

class Solution {
public:
    bool isLetterOrNumber(char ch)
    {
        if(('a'<=ch&&ch<='z')||('A'<=ch&&ch<='Z')||('0'<=ch&&ch<='9'))
        {
            return true;
        }
        return false;
    }

    bool isPalindrome(string s)
    {
        if(s.empty())
        {
            return true;
        }
        for(auto& ch:s)
        {
            if('a'<=ch&&ch<='z')
            {
                ch-=32;
            }
        }
        int begin=0;
        int end=s.size()-1;
        while(begin<end)
        {
            while(begin<end && !isLetterOrNumber(s[begin]))
            {
                begin++;
            }
            while(begin<end && !isLetterOrNumber(s[end]))
            {
                end--;
            }
            if(s[begin]!=s[end])
            {
                return false;
            }
            begin++;
            end--;
        }
        return true;
    }
};

字符串相加
在这里插入图片描述

class Solution {
public:
    string addStrings(string num1, string num2)
    {
        int end1=num1.size()-1;
        int end2=num2.size()-1;
        int value1=0;
        int value2=0;
        int next=0;
        string addret;
        while(end1>=0||end2>=0)
        {
            if(end1>=0)
            {
                value1=num1[end1--]-'0';
            }
            else
            {
                value1=0;
            }
            if(end2>=0)
            {
                value2=num2[end2--]-'0';
            }
            else
            {
                value2=0;
            }
            int valueret=value1+value2+next;
            if(valueret>9)
            {
                valueret-=10;
                next=1;
            }
            else
            {
                next=0;
            }
            addret+=(valueret+'0');
        }
        if(next==1)
        {
            addret+='1';
        }
        reverse(addret.begin(),addret.end());
        return addret;
    }
};

反转字符串||
在这里插入图片描述

class Solution {
public:
    string reverseStr(string s, int k)
    {
        int n=s.size();
        for(int i=0;i<n;i+=2*k)
        {
            reverse(s.begin()+i,s.begin()+min(i+k,n));
        }
        return s;
    }
};

反转字符串中的单词|||
在这里插入图片描述

class Solution {
public:
    string reverseWords(string s)
    {
        int size=s.size();
        int i=0;
        while(i<size)
        {
            int start=i;
            while(i<size&&s[i]!=' ')
            {
                i++;
            }
            int left=start;
            int right=i-1;
            while(left<right)
            {
                swap(s[left],s[right]);
                left++;
                right--;
            }
            while(i<size&&s[i]==' ')
            {
                i++;
            }
        }
        return s;
    }
};

反转字符串的单词|||
在这里插入图片描述

class Solution {
public:
    string reverseWords(string s)
    {
        int size = s.size();
        for (int i = 0; i < size;)
        {
            // 查找空格的位置
            size_t pos = s.find(' ', i);
            // 如果没有找到空格,则说明是最后一个单词
            if (pos == string::npos)
                pos = size;
            // 反转当前单词
            reverse(s.begin() + i, s.begin() + pos);
            // 更新 i,跳过空格
            i = pos + 1;
        }
        return s;
    }
};

字符串相乘
在这里插入图片描述

//竖式相乘法
class Solution {
public:
    string multiply(string num1, string num2)
    {
        int n1=num1.size();
        int n2=num2.size();
        string res(n1+n2,'0');
        for(int i=n2-1;i>=0;i--)
        {
            for(int j=n1-1;j>=0;j--)
            {
                int temp=(res[i+j+1]-'0')+(num1[j]-'0')*(num2[i]-'0');
                res[i+j+1]=temp%10+'0';//当前位
                res[i+j]+=temp/10; //前一位加上进位,res[i+j]已经初始化为'0',加上int类型自动转化为char,所以此处不加'0'
            }
        }
        //去除首位'0'
        for(int i=0;i<n1+n2;i++)
        {
            if(res[i]!='0')
                return res.substr(i);
        }
        return "0";
    }
};

找出字符串中第一个只出现一次的字符
在这里插入图片描述

#include <iostream>
#include<string>
using namespace std;

int main()
{
    string s;
    cin>>s;
    int count[256]={0};
    for(int i=0;i<s.size();i++)
    {
        count[s[i]]++;
    }
    for(int i=0;i<s.size();i++)
    {
        if(count[s[i]]==1)
        {
            cout<<s[i];
            return 0;
        }
    }
    cout<<-1;
    return 0;
}

4.string类的模拟实现

#pragma once
#include<assert.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
namespace ljh
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		const_iterator begin() const
		{
			return _str;
		}

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

		/*string()
			:_str(new char[1]{'\0'})
			,_size(0)
			,_capacity(0)
		{}*/

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

		// 传统写法
		// s2(s1)
		//string(const string& s)
		//{
		//	_str = new char[s._capacity+1];
		//	strcpy(_str, s._str);
		//	_size = s._size;
		//	_capacity = s._capacity;
		//}


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

		//		delete[] _str;
		//		_str = tmp;
		//		_size = s._size;
		//		_capacity = s._capacity;
		//	}

		//	return *this;
		//}

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

		// s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
		}

		// s2 = s3
		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		string tmp(s);
		//		//this->swap(tmp);
		//		swap(tmp);
		//	}

		//	return *this;
		//}

		// s2 = s3
		string& operator=(string tmp)
		{
			swap(tmp);

			return *this;
		}

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

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

			return _str[pos];
		}

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

			return _str[pos];
		}

		size_t capacity() const
		{
			return _capacity;
		}

		size_t size() const
		{
			return _size;
		}

		const char* c_str() const
		{
			return _str;
		}

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

				_capacity = n;
			}
		}

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

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

		size_t find(char ch, size_t pos = 0)
		{
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}

			return npos;
		}

		size_t find(const char* sub, size_t pos = 0)
		{
			const char* p = strstr(_str + pos, sub);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos, size_t len = npos)
		{
			string s;
			size_t end = pos + len;
			if (len == npos || pos + len >= _size) // 有多少取多少
			{
				len = _size - pos;
				end = _size;
			}

			s.reserve(len);
			for (size_t i = pos; i < end; i++)
			{
				s += _str[i];
			}

			return s;
		}

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

			_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+=(char ch)
		{
			push_back(ch);
			return *this;
		}

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

		// insert(0, 'x')
		void insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

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

			_str[pos] = ch;
			_size++;
		}

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

			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			// 挪动数据
			int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];
				--end;
			}

			strncpy(_str + pos, str, len);
			_size += len;
		}

		void erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);

			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t begin = pos + len;
				while (begin <= _size)
				{
					_str[begin - len] = _str[begin];
					++begin;
				}
				_size -= len;
			}
		}

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

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

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	public:
		const static size_t npos;
	};

	const size_t string::npos = -1;

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

		return out;
	}

	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		//s.reserve(128);

		char buff[129];
		size_t i = 0;

		char ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 128)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			//s += ch;

			ch = in.get();
		}

		if (i != 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}
}
  • 15
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

索隆43

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值