Lesson08---string(4)类

Lesson08—string类(4)

c++第八章string类的实现



前言

这篇文章写了计算机是怎么存储文字的,以及string类的底层是怎么实现的


一、计算机是怎么储存文字的

1. 在此之前先思考一个问题

#define _CRT_SECURE_NO_WARNINGS 
#include<bits/stdc++.h>
using namespace std;
int main()
{
    char buff1[] = "abcd";
    char buff2[] = "你好";
    cout << sizeof(buff1) << endl;
    cout << sizeof(buff2) << endl;

    return 0;
}

上面的代码输出是多少?


在这里插入图片描述
这就很奇怪为什么数量不一样需要的字节却是一样的?

为什么第一个数组明明只有4的字母要的内存却是5的?

通过调试来看看
在这里插入图片描述
这里可以看到第一个数组里面存的其实是ascll码,内存里面那个61.62其实就是97的16进制,比实际的数量多1是因为\0,中文是俩个字节

2.编码表

2.1 ascll码

在计算机里面只有010101,它也只认识0101010其他的语言它必须翻译成01010计算机它才能认识,最开始发明计算机的大佬用的是英语,大佬们就对英语进行了编码,对26个字母进行了映射,以及一些标点符号

在这里插入图片描述
在这里插入图片描述
计算机内存里面只会存ascii码,显示的时候就去找这个表然后显示出来


在这里插入图片描述
早年间计算机只有在美国那边使用,所以最开始就只弄了英文和一些标点符号,但随着计算机的普及一种语言早以满足不了人们

在这里插入图片描述
ascll码的取值范围是0-127,那么上面的负数到底是啥?


2.2unicode码

英文就26个大小一共也就26*2加上一些符号上面的加起来差不多也就100多个,所有的单词都是这些组成,那么中文呢?

汉字十几万个难道也去做一个十几万个的映射表?


unicode也就是统一码,它诞生就是为了编码全世界的文字
在这里插入图片描述
在这里插入图片描述
大部分的文字用俩个字节就足以存下,但这里又会出现一个新的问题,如果中英混合怎么办?


2.3UTF码

如果想实现混合编码那么这几套就必须要兼容,utf码提供了三种编码
在这里插入图片描述
用的最多的就是utf8

2.4gbk码

中华的文化博大精深就比如中文不仅有简体字还有繁体字utf就没有gbk处理中文处理的好,gbk就是专门处理中文的一种编码在这里插入图片描述

二、实现一个简单的string

1.构造函数和析构函数

我这里采用多文件的形式

//string.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class My_String
{
	friend ostream& operator<<(ostream& out, My_String& string);
public:
	My_String(const char* str="");
	~My_String();
	const char* c_str()const;
private:
	char* _str;
	size_t _size;
	size_t _capacity;

};








在这里插入图片描述


#include"string.h"
My_String::My_String(const char* str)
	:_size(strlen(str))

{

	_str = new char[_size + 1];
	_capacity = _size;
	strcpy(_str, str);
}

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

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

ostream& operator<<(ostream& out,  My_String& string)
{
	cout << string._str;
	return out;
}

在这里插入图片描述

2.范围for和迭代器

//string.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class My_String
{
	friend ostream& operator<<(ostream& out, My_String& string);
public:
	typedef char* iterator;


	iterator begin(); 

	iterator end();
	My_String(const char* str="");
	~My_String();
	const char* c_str()const;
	size_t size()const;
	char& operator[](size_t pos);

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

};


在这里插入图片描述

//string.cpp
#include"string.h"




My_String::iterator My_String::begin()
{
	return _str;
}

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


My_String::My_String(const char* str)
	:_size(strlen(str))

{

	_str = new char[_size + 1];
	_capacity = _size;
	strcpy(_str, str);
}

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

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

ostream& operator<<(ostream& out,  My_String& string)
{
	cout << string._str;
	return out;
}
size_t My_String::size()const
{
	return _size;
}
char& My_String::operator[](size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}

在这里插入图片描述
这里需要注意命名最好养成习惯,要不然就会出现各种各样的问题
在这里插入图片描述
这样以后就可以用迭代器和范围for来遍历这个数组了,范围for的底层就是迭代器

3.常见的成员函数

3.1 size

这个比较简单就不过多赘述
在这里插入图片描述

3.2 reserve

这个函数有保留的意思这里我拿来扩容,细节可以参考https://blog.csdn.net/m0_67371175/article/details/141872058?spm=1001.2014.3001.5502的第七条

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

在这里插入图片描述

3.3 push_back

这个函数在string容器里面比较复杂我这里就简单实现一下

void My_String::push_back(char ch)
{
	size_t newcapacity;
	if (_size == _capacity)
	{
		if (_capacity == 0)
		{
			newcapacity = 4;
		}
		else
		{
			newcapacity = _capacity * 2;
		}
		My_String::reserve(newcapacity);
	}

	_str[_size] = ch;
	_str[_size + 1] = '\0';
	++_size;
}

在这里插入图片描述

3.4 append

这里append和push_back的区别就是一个是字符一个是字符串这里我append是尾插字符串

void My_String::append(const char* str)
{
	size_t len = strlen(str);
	if (_size+len>_capacity)
	{
		reserve(_size + len);
	}
	strcpy(_str+_size, str);
	_size += len;
}

在这里插入图片描述

3.5 insert(insert(size_t pos, char ch))

下面代码运行结果是死循环,你找的到问题出在哪里吗
错误1:

void My_String::insert(size_t pos, char ch)
{
	if (_size == _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
		reserve(newcapacity);
	}

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

	}

	_str[pos] = ch;
	_size++;
	

}

在这里插入图片描述

在这里插入图片描述
因为这里的end是size_t类型没有符号所以直接就变成了最大的正数了
在这里插入图片描述
错误2:
在这里插入图片描述
把end换成int类型还是死循环你知道为什么吗?

在这里插入图片描述

int类型和size_t类型比较发生隐式类型转换,int类型转换成了size_t了
在这里插入图片描述
解决方法就是把pos强制转换成int
在这里插入图片描述
这样就可以解决这个问题
或者也可以这样
在这里插入图片描述

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

void My_String::insert(size_t pos, const char* str)
{
	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--;
	}

	_size += len;
	memcpy(_str + pos, str, len);
}
size_t len = strlen(str);

if (_size + len > _capacity)
{
	reserve(_size + len);
}

也可以这么写

size_t len = strlen(str);

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

while (end>= pos+len)
{
	_str[end]=_str[end - len];
	end--;
		
}
memcpy(_str + pos, str, len);
_size += len;

insert函数写好了以后尾插就可以直接调用这个函数了

3.7 void erase(size_t pos = 0, size_t len = npos);

//下面代码还有一个错误可以尝试找一下
void My_String::erase(size_t pos, size_t len)
{
	assert(pos < _size );
	if (pos+len>=_size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
}

在这里插入图片描述
但是这样写就没错

void My_String::erase(size_t pos, size_t len)
{
	assert(pos < _size );
	if (len>=_size - pos)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
}

在这里插入图片描述
我这里len的缺省值是npos,npos是size_t n=-1,然后它是无符号整型纯的补码,也就是111111…也就是size_t的最大值,如果在+一个数就会溢出

3.8 size_t find(char ch, size_t pos=0);

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

	return npos;
}

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

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

3.10 拷贝构造函数 My_String(const My_String& str);

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

3.11赋值函数 My_String& operator=(My_String s);

My_String& My_String::operator=(My_String s)
{
	if (this != &s) {
		char* temp = new char[s._capacity + 1];
		strcpy(temp, s._str);
		delete[] _str;
		_str = temp;
		_size = s._size;
		_capacity = s._capacity;
	}
	return *this;
}

3.12 My_String substr(size_t pos = 0, size_t len = npos);

My_String My_String::substr(size_t pos, size_t len)
{
	if (len > _size - pos)
	{
		My_String sub(_str + pos);
		return sub;
	}
	else
	{
		My_String sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += pos + i;
		}
		return sub;
	}
}

4.运算符重载

这 里只重载+=比较有意义就先重载这一个

My_String& My_String::operator+=(char ch)
{
	push_back(ch);
	return *this;

}

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
加上const以后就不再报错


5.比较运算符 重载

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

bool My_String::operator>(const My_String& s) const
{
	return !(*this <= s._str);
}

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

bool My_String::operator>=(const My_String& s) const
{
	return !(*this < s._str);
}

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

bool My_String::operator!=(const My_String& s) const
{
	return !(*this == s._str);
}

6.流插入和提取

void My_String::clear()
{
	_str[0] = '\0';
	_size = 0;
}
ostream& operator<<(ostream& out,  My_String& string)
{
	out << string._str;
	return out;
}
istream& operator>>(istream& in, My_String& string)
{
	string.clear();
	char ch = in.get();
	while (ch != ' ' && ch != '\n')
	{
		string += ch;
		ch = in.get();

	}
	return in;
}

7.string的优化

在这里插入图片描述
现在有一个问题假如我的string特别长那么我就需要一直效率就下去了,如果我一开始就扩容一个很大的空间又不行,假如我一开始给50个空间,那假如我只要2个字符那么又会浪费,这里可以用一个buff数组来解决这个问题

stream& operator>>(istream& in, My_String& string)
{
	string.clear();
	char ch = in.get();
	int i = 0;
	char buff[128];
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		ch = in.get();
		if (i == 127)
		{
			buff[i] = '\0';
			string += buff;
			i = 0;
		}

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

buff数组它是在栈上它就不会浪费很多空间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值