C++中string类的常见接口使用及模拟实现

C语言中,字符串是以’\0’结尾的一些字符的集合,C标准库提供了str的库函数,但这些库函数与字符串分离的

在C++中我们有string类来对字符串进行各种操作

1.string是表示字符串的字符串类
2,该类接口与常规接口基本相同,添加了一些专门用来操作string的常规操作
3.string在底层实现实际是:typedef bassic_string<char,char_traits,allocator> string 模板的别名
4.不能操作多字节或变长字符的序列
5.在使用string类是,必须包含#include< string>和using namespace std;

接下来就是常见接口使用说明

常见接口说明

1.string类对象的常见构造

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

在这里插入图片描述

2.string类对象的容量操作

size返回字符串有效字符长度
empty检测字符串释放为空串,是返回true,否则返回false
clear清空有效字符
reserve为字符串预留空间
resize将有效字符的个数改成n个,多出的空间默认用字符’\0’填充
length返回字符串有效字符长度
capacity返回空间总大小

注意:

1.string类对象支持直接用cin和cout进行输入和输出
2.使用clear将s中的字符串清空,只是将size清0,不改变底层空间大小
3.resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大
小,如果是将元素个数减少,底层空间总大小不变
4.size()和length()方法底层实现原理基本相同,为了与其他接口保持一致,常用size()
5.reserve(size_t res_arg=0),为string预留空间,不改变有效元素个数,当参数小于string底层空间总大小时,reserve不改变容量大小

在这里插入图片描述
以下是reserve的一些操作

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.string类对象的访问及遍历

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

下标的访问
在这里插入图片描述

三种遍历方式
在这里插入图片描述

4.string类对象的修改操作

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

在这里插入图片描述

5.string类非成员函数

函数功能说明
operatoe>>输入运算符重载
operator<<输出运算符重载
getline获取一行运算符
relational operators大小比较
operator+尽量少用,因为传值拷贝效率低

vs和g++下string结构的区别

vs下string的结构

string总共占28个字节,内部有个联合体,联合体来定义string中字符串的存储空间
1.当字符串长度小于 16,使用内部固定的字符数组来存放
2.当字符串长度大于等于16时,从堆上开辟空间
3.一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量,还有个指针做其他事
在这里插入图片描述

g++下的结构

g++下,string通过写时拷贝,string对象总共占4个字节,内部只包含了一个指针,该指针指向一块堆空间
在这里插入图片描述

string类的模拟实现

在模拟实现时,如果没有显示定义其拷贝构造函数与赋值运算符重载,此时编译器自动生成,当s1拷贝构造s2时,会导致s1,s2共用一块内存空间,在释放内存空间时会导致内存崩溃,也是浅拷贝问题

以下是string类模拟实现的正确写法

#pragma once

#define _CRT_SECURE_NO_WARNINGS 1

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

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

		string(const string& s)
			: _str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			this->swap(tmp);
		}

		string& operator=(string s)
		{
			this->swap(s);
			return *this;
		}

		~string()
		{
			if (_str)
			{
				delete[] _str;
				_str = nullptr;
			}
		}

		/
		// iterator
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		/
		// modify
		void push_back(char c)
		{
			if (_size == _capacity)
				reserve(_capacity * 2);

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

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

		// 作业实现
		void append(const char* str);
		string& operator+=(const char* str);

		void clear()
		{
			_size = 0;
			_str[_size] = '\0';
		}

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

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

		/
		// capacity
		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _capacity;
		}

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

		void resize(size_t newSize, char c = '\0')
		{
			if (newSize > _size)
			{
				// 如果newSize大于底层空间大小,则需要重新开辟空间
				if (newSize > _capacity)
				{
					reserve(newSize);
				}

				memset(_str + _size, c, newSize - _size);
			}

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

		void reserve(size_t newCapacity)
		{
			// 如果新容量大于旧容量,则开辟空间
			if (newCapacity > _capacity)
			{
				char* str = new char[newCapacity + 1];
				strcpy(str, _str);

				// 释放原来旧空间,然后使用新空间
				delete[] _str;
				_str = str;
				_capacity = newCapacity;
			}
		}

		
		// access
		char& operator[](size_t index)
		{
			assert(index < _size);
			return _str[index];
		}

		const char& operator[](size_t index)const
		{
			assert(index < _size);
			return _str[index];
		}

		
		// 作业
		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,并返回该字符的位置
		string& insert(size_t pos, char c);
		string& insert(size_t pos, const char* str);

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

	private:
		friend ostream& operator<<(ostream& _cout, const kai::string& s);
		friend istream& operator>>(istream& _cin, kai::string& s);
	private:
		char* _str;
		size_t _capacity;
		size_t _size;
	};

	ostream& operator<<(ostream& _cout, const kai::string& s)
	{
		// 不能使用这个, 因为string的字符串内部可能会包含\0
		// 直接cout时, 是将_str当成char*打印的,遇到内部的\0时后序内容就不打印了
		//cout << s._str;
		for (size_t i = 0; i < s.size(); ++i)
		{
			_cout << s[i];
		}
		return _cout;
	}
}

///对自定义的string类进行测试
void TestKaistring()
{
	kai::string s1("hello");
	s1.push_back(' ');
	s1.push_back('k');
	s1.push_back('a');
	s1 += 'i';
	cout << s1 << endl;
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	// 利用迭代器打印string中的元素
	kai::string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it;
		++it;
	}
	cout << endl;

	// 这里可以看到一个类只要支持的基本的iterator,就支持范围for
	for (auto ch : s1)
		cout << ch;
	cout << endl;
}

在这里插入图片描述

深浅拷贝

深拷贝

如果一个类中涉及资源的管理,其拷贝构造函数,赋值运算符重载以及析构函数必须显示给出。一般按照深拷贝提供,每个对象都有一份独立的资源,不需要和其他对象共享。
(就是重新开辟空间,拷贝一个)
在这里插入图片描述

浅拷贝

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

当进行管理资源时,有违规时可以考虑深拷贝

写时拷贝

写时拷贝是在浅拷贝的基础上增加了引用计数的方式来实现的。
引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数加1,当有对象被销毁时,先给计数减一,再检查是否需要释放资源,如果计数为1,说明该对象是最后一个使用者,将该资源释放,否则不能释放。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值