c++/string类

目录

1.为什么要学习string类?

 2.标准库中的string类

2.1string类的文档介绍

 2.2string类的基本概念

 2.3string类的常见接口方法

a.常见的构造函数

b.string类对象的容量操作

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

 d.string类对象的修改操作

 3.string类相关题目

 4.string的模拟实现

 

3.1浅拷贝

 3.2深拷贝

 3.3模拟实现代码

 string.h

 string.cpp


早上好,中文好,晚上好

1.为什么要学习string类?

在 C语言 中,字符串是以\0结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP(面向对象)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问
于是 C++ 中就引入了String类,它可以看做是一个管理字符串的数据结构。

 2.标准库中的string类

2.1string类的文档介绍

 cplusplus.com/reference/string/string/?kw=string

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

 2.2string类的基本概念

string 就是字符串的意思,是 C++用来代替char数组的数据结构。里面封装了一些常用的方法,方便我们地对其进行一些操作,而且string的空间大小是动态变化的,大大减小了不必要的花销 

 2.3string类的常见接口方法

a.常见的构造函数

string()  : 构造空的string类对象,即空字符串
string(const char* s)  : 用C-string来构造string类对象
string(size_t n, char c)  : string类对象中包含n个字符c
string(const string&s)  : 拷贝构造函数
~string():析构函数
void Teststring()
{
string s1; // 构造空的string类对象s1
string s2("hello bit"); // 用C格式字符串构造string类对象s2
string s3(s2); // 拷贝构造s3
}

b.string类对象的容量操作

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(n)用0来填充多出的元素空间,resize(size_t n, char
c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数
增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
4. reserve(size_t res_arg=0):为string 预留空间 ,不改变有效元素个数,当reserve的参
数小于string的底层空间总大小时,reserver不会改变容量大小。

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

operator[] :返回pos位置的字符,const string类对象调用
begin+ end :begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin + rend :begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
范围for :C++11支持更简洁的范围for的新遍历方式
int main()
{
	string s1("hello world");
	//string遍历(三种方式)
	
	//下标+[]
	for (size_t i = 0; i < s1.size(); i++)
	{
		s1[i]++;//修改
		cout << s1[i] << " ";
	}
	cout << endl;
	//迭代器 --- 像指针一样的对象
	string::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
		(*it1)--;//修改
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;
	//范围for
	for (auto ch : s1)
	{
		cout << ch << " ";
	}
	cout << endl;
	for (auto ch : s1)
	{
		ch++;
		cout << ch << " ";
	}

	//auto 自动推导类型
	int i = 0;
	int j = i;
	auto z= i;//int
	auto x = 1.1;//double
	auto& r1 = i;
	auto r2 = r1;//int
	auto& r3 = r1;//int&
	//auto r4; //报错

	//范围for  遍历数组
	int a[] = { 1,2,3,4,5,6 };
	for (size_t i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;

	for (auto e : a)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

 d.string类对象的修改操作

push_back :在字符串后 尾插 字符c
append :在字符串后 追加 一个字符串
operator+= :在字符串后 追加字符串 str
c_str:返回C格式字符串
find + npos:从字符串pos位置开始 往后找字符c ,返回该字符在字符串中的 位置
rfind :从字符串pos位置开始 往前找字符c ,返回该字符在字符串中的 位置
substr :在str中从pos位置开始,截取 n个字符 ,然后将其返回

 3.string类相关题目

917. 仅仅反转字母 - 力扣(LeetCode)

 

class Solution {
public:

    bool isLetter(char ch)
    {
        if (ch >= 'a' && ch <= 'z')
            return true;
        if (ch >= 'A' && ch <= 'Z')
            return true;
        return false;
    }
    string reverseOnlyLetters(string s) {
        if (s.empty())
            return s;
        size_t begin = 0, end = s.size() - 1;
        while (begin < end)
        {
            while (begin < end && !isLetter(s[begin]))
                ++begin;
            while (begin < end && !isLetter(s[end]))
                --end;
            swap(s[begin], s[end]);
            ++begin;
            --end;
        }
        return s;
    }
};

387. 字符串中的第一个唯一字符 - 力扣(LeetCode) 

 

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

 125. 验证回文串 - 力扣(LeetCode)

 

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

    bool isPalindrome(string s) {
        int begin=0;
        int end=s.size()-1;
        while(begin<end)
        {
            while(begin<end && !isln(s[begin]))
            {
                ++begin;
            }
            while(begin<end && !isln(s[end]))
            {
                --end;
            }
            char left=std::tolower(s[begin]);
            char right=std::tolower(s[end]);
            if(left!=right)
            {
                return false;
            }
            ++begin;
            --end;
        }
        return true;
    }
};

 4.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 world");
String s2(s1);
}

说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认
的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内
存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝

 

3.1浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来 。如果 对象中管理资源 ,最后就会 导致 多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该 资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规
就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一
不想分享就你争我夺,玩具损坏。

 

可以采用深拷贝解决浅拷贝问题,即: 每个对象都有一份独立的资源,不要和其他对象共享 。父
母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。

 3.2深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

 

 3.3模拟实现代码

 string.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once

#include<iostream>
#include<assert.h>
#include<string>
using namespace std;
namespace pzn
{
	class string
	{
	public:
		//构造函数
		string(const char* str = "");
		//拷贝构造
		string(const string& s);
		//析构函数
		~string();
		//分配内存,预留内存
		void reserve(size_t n);
		//尾插一个字符
		void push_back(char ch);
		//尾插一个字符串
		void append(const char* str);
		//重载+=
		string& operator+=(char ch);
		//重载+= const
		string& operator+=(const char* str);
		//迭代器
		using iterator = char*;
		//迭代器 const
		using const_iterator = const char*;

		//string();
		string& operator=(const string& s);

		//在pos位置插入一个字符
		void insert(size_t pos, char ch);
		//在pos位置插入一个字符串
		void insert(size_t pos, const char* str);
		//在pos位置删除数据
		void erase(size_t pos, size_t len = npos);//只能在声明中给缺省值
		//查找字符
		size_t find(char ch, size_t pos = 0);
		//查找字符串
		size_t find(const char* str, size_t pos = 0);
		char& operator[](size_t i)
		{
			assert(i < _size);
			return _str[i];
		}
		//[]重载
		const char& operator[](size_t i) const
		{
			assert(i < _size);
			return _str[i];
		}
		//迭代器
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin() const
		{
			return _str;
		}
		const_iterator end() const
		{
			return _str + _size;
		}
		size_t size() const
		{
			return _size;
		}
		const char* c_str() const
		{
			return _str;
		}

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		//提取pos位置之后长度为len的字符
		string substr(size_t pos, size_t len=npos);
		static const size_t npos;

		
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
	//定义在全局
	bool operator== (const string& lhs, const string& rhs);
	bool operator!= (const string& lhs, const string& rhs);
	bool operator> (const string& lhs, const string& rhs);
	bool operator< (const string& lhs, const string& rhs);
	bool operator>= (const string& lhs, const string& rhs);
	bool operator<= (const string& lhs, const string& rhs);

	ostream& operator<<(ostream& os, const string& str);
	istream& operator>>(istream& is, string& str);
}

 string.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"

namespace pzn
{
	const size_t string::npos = -1;
	//构造函数
	string::string(const char* str)
		:_size(strlen(str))
	{
		_capacity = _size;
		_str = new char[_size + 1];

		strcpy(_str, str);
	}
	//拷贝构造函数
	string::string(const string& s)
	{
		_str = new char[s._capacity + 1];
		//复制原字符串到新分配的内存中
		strcpy(_str, s._str);
		_size = s._size;
		_capacity = s._capacity;
	}
	string& string::operator=(const string& s)
	{
		if (this != &s)
		{
			delete[] _str;
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}
		return *this;
	}
	string::~string()
	{
		delete[] _str;
		_size = 0;
		_capacity = 0;
	}
	//分配内存
	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];//预留\0位置,容量不包括\0
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}
	//尾插一个字符
	void string::push_back(char ch)
	{
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		_str[_size] = ch;
		_size++;
	}
	//尾插字符串
	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			size_t newCapacity = 2 * _capacity;
			//扩容2倍不够,则需要多少扩多少
			if (newCapacity < _size + len)
				newCapacity = _size + len;
			reserve(newCapacity);
		}
		strcpy(_str + _size, str);//将新字符串复制到当前字符串末尾
		_size += len;
	}
	//重载+=
	string& string::operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	//重载+= const
	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}
	//在指定的位置插入字符
	void string::insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size == _capacity)
		{
			//扩容
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		size_t end = _size + 1;
		while (end > pos)
		{
			//数据往前挪
			_str[end] = _str[end - 1];
			--end;
		}
		_str[pos] = ch;//插入
		_size++;
	}
	//在pos位置插入一个字符串
	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			size_t newCapacity = 2 * _capacity;
			//扩2倍不够,需多少扩多少
			if (newCapacity < _size + len)
				newCapacity = _size + len;
			//预留空间
			reserve(newCapacity);
			size_t end = _size + len;//插入后字符串的结尾位置
			while (end > pos + len - 1)//移动字符,为插入字符串腾出空间
			{
				_str[end] = _str[end - len];
				--end;
			}
			for (size_t i = 0; i < len; i++)
			{
				_str[pos + i] = str[i];//将str中的字符串逐一复制到目标位置
			}
			_size += len;//增加字符串的大小
		}
	}
	//在pos位置删除数据
	void string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);
		if (len >= _size - pos)//如果要删除的长度大于或等于从pos开始到字符串末尾的长度
		{
			_str[pos] = '\0';//将删除位置的字符设为'\0'
			_size = pos;
		}
		else
		{
			//从后往前移动字符
			size_t end = pos + len;//计算开始移动的字符位置,即要删除的字符串的最后位置
			while (end <= _size)
			{
				_str[end = len] = _str[end];
				++end;
			}
			_size -= len;
		}
	}
	//查找字符
	size_t string::find(char ch, size_t pos)
	{
		assert(pos < _size);
		for (size_t i = pos; i < _size; i++)
		{
			if (ch == _str[i])
				return i;
		}
		return npos;
	}
	//查找字符串
	size_t string::find(const char* str, size_t pos)
	{
		assert(pos < _size);
		//使用strstr在字符串_str中,从pos位置开始查找str
		const char* ptr = strstr(_str + pos, str);
		if (ptr == nullptr)
		{
			return npos;
		}
		else
		{
			return ptr - _str;
		}
	}
	//提取pos位置之后长度为len的字符
	string string::substr(size_t pos, size_t len)
	{
		assert(pos < _size);
		//如果请求的长度len大于从pos到结尾的剩余长度
		if (len > (_size - pos))
		{
			len = _size - pos;//设置len为剩余长度
		}
		pzn::string sub;
		//预留len的空间,避免在构造过程中多次分配内存
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)//将原字符串从 pos 开始的 len 个字符逐个添加到 sub 中
		{
			sub += _str[pos + i];
		}
		return sub;
	}
	bool operator== (const string& lhs, const string& rhs)
	{
		return strcmp(lhs.c_str(), rhs.c_str()) == 0;
	}
	bool operator!= (const string& lhs, const string& rhs)
	{
		return !(lhs == rhs);
	}
	bool operator> (const string& lhs, const string& rhs)
	{
		return !(lhs <= rhs);
	}
	bool operator< (const string& lhs, const string& rhs)
	{
		return strcmp(lhs.c_str(), rhs.c_str()) < 0;
	}
	bool operator>= (const string& lhs, const string& rhs)
	{
		return !(lhs < rhs);
	}
	bool operator<= (const string& lhs, const string& rhs)
	{
		return lhs < rhs || lhs == rhs;
	}

	ostream& operator<<(ostream& os, const string& str)
	{
		for (size_t i = 0; i < str.size(); i++)
		{
			os << str[i];
		}
		return os;
	}
	istream& operator>>(istream& is, string& str)
	{
		str.clear();//清空字符串
		char ch;
		ch = is.get();//获取一个字符
		while (ch != '\n' && ch != '\0')
		{
			str += ch;
			ch = is.get();
		}
		return is;
	}
}

感谢观看

评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

pzn)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值