您的位置:首页 > 游戏 > 手游 > 直播软件app开发制作_遵义网站建设公司_西安关键字优化哪家好_腾讯广告投放推广平台

直播软件app开发制作_遵义网站建设公司_西安关键字优化哪家好_腾讯广告投放推广平台

2025/3/19 6:31:51 来源:https://blog.csdn.net/2301_81250340/article/details/146163064  浏览:    关键词:直播软件app开发制作_遵义网站建设公司_西安关键字优化哪家好_腾讯广告投放推广平台
直播软件app开发制作_遵义网站建设公司_西安关键字优化哪家好_腾讯广告投放推广平台

一、前言

  在我们对STL中的string类有了一个基本的认识后,今天我们手动的从0到1去模拟实现一个STL库中的string类中的一些常用接口。

二、string类的模拟实现

为了不和库里的string类产生冲突,我们可以建立一个名为其他的命名空间,此时因为作用域不同,就不会产生冲突。即namespace xxx.

下面是对string类的声明:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<assert.h>
using namespace std;namespace zihan//命名空间
{class string//定义类{public://构造string(const char* str = " ")//全缺省{_size = strlen(str);_capacity = _size;//_capacity中不包含\0,为了存放字符 \0  需要长度加一_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;}//赋值 S1=S2string& 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(){delete[]_str;_str = nullptr;_size = _capacity = 0;}const char* c_str() const{return _str;}size_t size()const{return _size;}size_t capacity()const{return _capacity;}//迭代器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;}//返回pos位置的字符const char& operator[](size_t pos)const{return _str[pos];}//清掉所有的数据但不动空间void clear(){_str[0] = '\0';_size = 0;}//预留空间void reserve(size_t n);//尾插字符void push_back(char ch);//尾插字符串void append(const char* str);//+=string& operator+=(char ch);string& operator+=(const char* str);//头插void insert(size_t pos,char ch);void insert(size_t pos, const char* str);void erase(size_t pos, size_t len = npos);//查找字符、字符串size_t find(char ch, size_t pos = 0);size_t fing(const char* str, size_t pos = 0);//截取字符串string substr(size_t pos, size_t len);//比较bool operator<(const string& s)const;bool operator<=(const string& s)const;bool operator>(const string& s)const;bool operator>=(const string& s)const;bool operator==(const string& s)const;bool operator!=(const string& s)const;private:char* _str = nullptr;size_t _capacity = 0;size_t _size = 0;static const size_t npos;};//流插入 流提取ostream& operator<<(ostream& out, const string& s);istream& operator>>(istream& out, string& s);
}

2.1成员函数

2.1.1构造函数

string(const char* str = " ")

string(const char* str = " ")//全缺省{_size = strlen(str);_capacity = _size;//_capacity中不包含\0,为了存放字符 \0  需要长度加一_str = new char[_capacity + 1];strcpy(_str, str);//拷贝字符串}

2.1.2拷贝构造函数

string(const string& s)

我们知道在拷贝构造中,若是一个类在没有显示定义拷贝构造,对于内置类型不做处理,对于自定义类型会去调用类中默认提供的拷贝构造函数,此时就会造成浅拷贝问题。

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

深拷贝:源对象和拷贝对象相互独立,其中一个对象的改动不会对另一个对象造成影响

若想进行深拷贝,必须显示定义拷贝构造

1.传统写法

//显示调用拷贝构造//S2(S1)string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}

传统写法的主要思想是开辟一块足够的空间,然后将源对象的字符串拷贝过去,接着把源对象的其他成员变量也拷贝过去即可。 

2.现代写法

void swap(string& s)
{std::swap(_str , s._str);//调用标准库中的swap函数将对象的成员变量进行交换成员变量std::swap(_size , s._size);std::swap(_capacity , s._capacity);
}
//现代写法
string(const string& s)
{string tmp(s._str);//带参构造swap(tmp);//通过交换去掠夺
}

现代写法的主要思想是先根据原字符串调用构造函数构造一个tmp对象,然后再将tmp对象与拷贝对象进行数据交换。 

2.1.3赋值运算符重载函数

string& operator=(const string& s)

1.传统写法

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;}

2.现代写法

//S1=S2
//现代写法
string& operator=(const string& s)
{//不能自己赋值自己if (this != &s){string tmp(s._str);swap(tmp);}return *this;
}

这里通过用源对象字符串构造一个tmp对象, tmp对象与s1交换之后,出了作用域也会销毁,可以达到将s2赋值给s1的效果。

2.1.4析构函数 

~string()

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

2.2元素访问

2.2.1迭代器

  本质上是封装思想,可以类比指针,可以指向容器中的某个元素,并可以通过操作迭代器来访问和修改容器中的元素 。

//迭代器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;}

2.2.2 返回pos位置的字符

const char& operator[](size_t pos)const

//返回pos位置的字符const char& operator[](size_t pos)const{return _str[pos];}

2.3字符串修改

2.3.1push_back 尾插字符

//尾插字符
void string::push_back(char ch)
{//可能要扩容if (_capacity == _size){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;++_size;//不要忘记\0_str[_size] = '\0';
}

2.3.2append尾插字符串

//尾插字符串
void string::append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){//扩容  如果长度大于2倍  则该多大就开多大 否则就开2倍的空间reserve(_size + len > _capacity * 2 ? _size + len : 2 * _capacity);}strcpy(_str + _size, str);_size += len;
}

2.3.3operator+=(char ch)

复用push_back即可

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

2.3.4operator+=(const char* str)

复用append即可

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

2.3.5在pos位置插入字符insert

void string::insert(size_t pos, char ch)
{assert(pos <= _size);//万一要扩容if (_capacity == _size){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size;//进行挪动 end 挪向end+1while (end >=  (int)pos){_str[end + 1] = _str[end];--end;}_str[pos] = ch;++_size;}

注意这里挪动数据时的while循环的判断条件end>=pos,因为end和pos都是size_t无符号类型,所以当_size=0时,end无论怎么— —都不会小于_size,也就会陷入死循环 。

这里有解决方法:1.把无符号类型强转为有符号类型,进行整型提升

                             2.令end=_size+1,循环条件变成>

2.3.6在pos位置插入字符串insert

void string::insert(size_t pos, const char* str)
{assert(pos <=_size);size_t len = strlen(str);if (_size + len > _capacity){//扩容  如果长度大于2倍  则该多大就开多大 否则就开2倍的空间reserve(_size + len > _capacity * 2 ? _size + len : 2 * _capacity);}size_t end = pos + len;while (end >= pos+len){_str[end] = _str[end - len];end--;}//再依次把字符串插进去for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;}

2.3.7删除pos之后的n个字符

void string::erase(size_t pos, size_t len )

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

这里分两种情况:

1.如果len>_size-pos,说明pos之后的全删除,只需把pos位置置为'\0'

2.否则需要将不删除的字符一个个往前挪动 

2.4比较运算符

2.4.1大于>

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

2.4.2小于<

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

2.4.3等于==

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

2.4.4大于等于>=

这里可以复用>和=,也可以对<取反

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

2.4.5小于等于<=

同理,这里可以复用<和=,也可以对>取反

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

2.4.6不等于!=

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

2.5查找和截取

2.5.1find,寻找一个字符

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

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

2.5.2find,寻找一个字符串

size_t fing(const char* str, size_t pos = 0);

这里直接使用C语言中的库函数strstr函数

size_t string::fing(const char* str, size_t pos)
{const char* ptr = strstr(_str + pos, str);//str在_str+pos中的位置if (ptr){return ptr - _str;//找到了,返回下标,所以指针相减}return npos;//没找到}

2.5.3substr,截取字符串 

string string::substr(size_t pos, size_t len)

//截取字符串
string string::substr(size_t pos, size_t len)
{if (len > _size - pos)//大于有效字符个数{   //更新lenlen = _size - pos;}//最后需要返回字符串string sub;sub.reserve(len);for (size_t i = 0; i < len; i++){//把要截取的字符尾插到subsub += _str[pos + i];}return sub;
}

在更新好有效字符个数后,从pos位置取n个字符追加到临时的string对象中去, 最后将其返回即可,这里我们返回了一个出作用域就销毁的临时变量,只能使用传值返回,不能使用传引用返回。

 2.6空间操作

2.6.1size

获取当前字符串的有效长度,不包含'\0'

size_t size()const
{return _size;
}

2.6.2capacity

获取字符串的当前的容量

size_t capacity()const
{return _capacity;
}

2.6.3clear清空数据

清掉所有的数据但不动空间

//清掉所有的数据但不动空间
void clear()
{_str[0] = '\0';_size = 0;}

2.6.4reserve预留空间,扩容

void string::reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];//开出一块新的空间,永远要多开一个空间 存\0strcpy(tmp, _str);//将原本的数据拷贝过来delete[]_str;//释放旧空间_str = tmp;//指向新空间_capacity = n;}
}

2.7流插入,流提取

2.7.1operator<< 流插入

ostream& operator<<(ostream& out, const string& s)
{for (auto ch: s)//范围for遍历{out << ch;}return out; 
}

注意这里不用加友元,因为并没有访问私有成员变量 

2.7.2operator>> 流提取

istream& operator>>(istream& in, string& s)
{s.clear();//清理字符串char ch = in.get();//一个一个字符获取while (ch != ' ' && ch != '\n'){s += ch;//将读取到的字符插到字符串后面ch = in.get();//继续读取字符串}return in;}

可以进行优化,遇到长度比较大的字符串时可以减少扩容次数

	istream& operator>>(istream& in, string& s){s.clear();const int n = 256;char buff[n];char ch = in.get();int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i = n - 1)//如果字符存满了{buff[n - 1] = '\0';s += buff;i = 0;//继续循环 直到读完}if (i > 0)//如果没有读满数组 还有剩余空间{buff[i] = '\0';s += buff;}return in;}if (i > 0){buff[i] = '\0';s += buff;}return in;}
}

样例测试:

以上就是string类基本接口的模拟实现,有不足的地方还请大家指正,共同交流! 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com