当前位置: 首页 > news >正文

【C++】模拟实现string类

通过上篇我们已经学习到了string类的基本使用,这里我们就试着模拟实现一些,我们主要实现一些常用到的函数。

注意:我们在此实现的和C++标准库中实现的有所不同,其目的主要是帮助大家大概理解底层原理。本篇中涉及许多的字符串函数,大家如果不清楚具体功能的可以先看一下这篇文章 -> 常见的字符串函数

我们模拟string类的大致框架是:

class string
{
public://...private:char* _str;size_t _size;size_t _capacity;
};

_str是在堆上动态开辟的空间,_size是元素个数,_capacity是容量大小。 接下来主要是实现一些public下的一些内容。

一、构造函数

在这里我们只实现两个常用的构造函数,一个是默认构造,一个是带参构造。

string():_str(nullptr),_size(0),_capacity(0)
{}
string(const char* str)
{_size = strlen(str);_capacity = _size; //_capacity不包含'\0',所以不用+1_str = new char[_capacity + 1]; //+1是为了存放'\0'strcpy(_str, str);
}

大家仔细看看,默认构造写的对吗?

 乍一看,没有任何问题,如果打印可以看到的是空串就说明我们写的是对的。

因为我们还没实现重载流插入和流提取,所以我们可以简单的写一个c_str()来帮助我们打印。

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

在主函数中调用test_string1():

void test_string1()
{string s1;string s2("hello world");cout << s1.c_str() << endl;cout << s2.c_str() << endl;
}

运行结果

我们发现程序崩了,这说明我们写的有问题,其原因是因为在默认构造函数中_str初始化为空指针nullptr, 在打印过程中,在字符串必须要找到'\0'才终止,而对nullptr进行解引用程序就会出现崩溃。

我们可以这样写:

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

运行结果: 

注意: _str(new char[1]{ '\0' })  和 _str(new char('\0'))这两种写法的效果一样,但要写成前者的形式,其目的是为了适应析构函数。

我们也可以将上边两个构造函数合并成一个:

string(const char* str = "")
{_size = strlen(str);_capacity = _size; //_capacity不包含'\0',所以不用+1_str = new char[_capacity + 1]; //+1是为了存放'\0'strcpy(_str, str);
}

二、析构函数

直接看代码即可:

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

三、赋值重载

string& operator=(const string& str)
{if(this != &str) //防止自己给自己赋值{delete[] _str; //先释放旧空间,否则会造成内存泄漏_str = new char[str._size + 1];strcpy(_str,str._str);_size = str._size;_capacity = str._capacity;}return *this;
}

四、拷贝构造

拷贝构造在"五(11)"引出。

五、成员函数

(1)size()/capacity()
//返回字符串中元素个数
size_t size() const
{return _size;
}//返回所开空间的容量大小(比实际空间少1,1就是'\0')
size_t capacity() const
{return _capacity;
}

这种简单的代码,大家一看便知,不过多赘述。

(2)重载[]
//返回下标为pos位置上的字符
char& operator[](size_t pos)
{assert(pos >= 0 && pos < _size); //越界直接报错return _str[pos];
}

这里需要注意的是返回值要用引用返回,其一,它的空间申请在堆上,调用[]后,对象不销毁,所用可以使用引用返回。其二,我们可以修改pos下标的值,即可读可写。

还有一种写法:

//返回const类型字符串下标为pos位置上的字符
const char& operator[](size_t pos) const
{assert(pos >= 0 && pos < _size); //越界直接报错return _str[pos];
}

这种写法只能读不能写,也就是不能修改pos下标的值。

调用assert函数必须包含头文件<assert.h>。 

(3)迭代器

我们在上篇文章中提到迭代器的功能像指针,所以在这里可以用指针的方式定义迭代器。

typedef char* iterator;//返回起始位置指针
iterator begin()
{return _str;
}//返回最后一个有效字符位置的下一位置的指针
iterator end()
{return _str + _size;
}

在主函数中调用test_string2():

void test_string2()
{string s1("good morning");string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;
}

运行结果:

 

 我们这里用原生指针来typedef迭代器,根本原因是string的底层是数组,我们才可以这样玩,如果底层是链表这样定义的方式就不行了。

迭代器是一种封装的体现。它屏蔽了底层结构和实现细节,提供了统一的类似访问容器的方式。

const迭代器:

typedef const char* const_iterator;//返回const类型字符串的起始位置指针
const_iterator cbegin() const
{return _str;
}//返回const类型字符串的最后一个有效位置的下一位置的指针
const_iterator cend() const
{return _str + _size;
}
(4)reserve()
//设置容量
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1]; //加1是为了存放'\0'strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}

n比原先容量大就扩容,在C++中我们尽量不要去使用C语言中的realloc去扩容,要使用new这个关键字来扩容。

n比原先容量小是否缩容是不确定的,我们这里就不处理这种情况了。

(5)push_back()
//在字符串最后位置上插入一个字符
void push_back(char ch)
{if (_size == _capacity){//扩容reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;
}

这里只实现了尾插一个字符这一个重载函数。

在主函数中调用test_string3():

void test_string3()
{string s1("hello world");s1.push_back('x');s1.push_back('x');cout << s1.c_str() << endl;
}

运行结果: 

这结果怎么和我们想象的不太一样? 怎么出现乱码了

这是因为我们没有考虑'\0',我们在尾插时覆盖掉了末尾的'\0',导致出现乱码。

更改后的代码:

void push_back(char ch)
{if (_size == _capacity){//扩容reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';  //防止'\0'被覆盖
}
(6)重载+=
//在字符串最后位置上接一个字符
string& operator+=(char ch)
{push_back(ch);return *this;
}

这里直接复用push_back,就可以达到效果。

在主函数中调用test_string4():

void test_string3()
{string s1("hello world");s1 += 'x';s1 += 'x';cout << s1.c_str() << endl;
}

运行结果: 

这里我们调用了修改后的push_back(),所以没有出现乱码,如果我们自己实现就要提防出现乱码的情况。 

这里通常也会+=一个字符串(我们可以复用下面append函数):

//在字符串最后位置上接一个字符串
string& operator+=(const char* str)
{append(str);return *this;
}
(7)append()
//在字符串最后位置上追加一个字符串
void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){//扩容//大于2倍,需要多少开多少,不足2倍,按二倍扩reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}strcpy(_str + _size, str); //这里不用担心'\0'问题,因为strcpy也会把'\0'拷贝进去_size += len;
}

append在扩容时不能直接固定2倍扩,因为追加的字符串的长度可能大于capacity的2倍,我们这里采用了如果_size + len大于2倍_capacity,需要多少开多少,不足2倍,按二倍扩。

(8)insert()
//在pos位置前插入一个字符
void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){//扩容reserve(_capacity * 2 == 0 ? 4 : _capacity * 2);}//挪动数据size_t end = _size;while (end >= pos){_str[end + 1] = _str[end];--end;}_str[pos] = ch;++_size;
}

代码写起来飞快,但有没有错误呢?

我们来验证一下, 在主函数中调用test_string5():

void test_string5()
{string s("hello world");cout << s.c_str() << endl;s.insert(0, 'x'); //头插cout << s.c_str() << endl;}

运行结果:

 

这里程序崩了,这又是哪里出问题了呢?

这是循环的跳出条件是end>=pos,正常情况下end==0是最后一次进入循环,然后end-1 == -1后大于0,就跳出循环,但由于这里end是size_t无符号整形,所以它永远不可能是-1,所以程序崩溃。那我们把end类型改为int不就行了 ,答案也是不行,因为pos是size_t类型,一个int类型和size_t类型进行比较时,会隐式类型提升,将int默默提升为size_t类型进行比较,所以我们要将pos强制转换成int类型进行比较。

修改后的代码:

//在pos位置前插入一个字符
void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){//扩容reserve(_capacity * 2 == 0 ? 4 : _capacity * 2);}//挪动数据int end = _size; //必须将end类型改为intwhile (end >= (int)pos)  //比较时必须将pos类型强制转换为int{_str[end + 1] = _str[end];--end;}_str[pos] = ch;++_size;
}

运行结果:

还有另外一种写法:

//在pos位置前插入一个字符
void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){//扩容reserve(_capacity * 2 == 0 ? 4 : _capacity * 2);}//挪动数据size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;
}

这种写法会更好一点。 

上面写的是插入一个字符,接下来我们实现插入一个字符串:

//在pos位置前插入一个字符串
void insert(size_t pos, const char* str)
{assert(pos < _size);size_t len = strlen(str);if (_size + len > _capacity){//扩容//大于2倍,需要多少开多少,不足2倍,按二倍扩reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}size_t end = _size + 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;
}

这段代码有一种情况也会出现问题,就是pos和len同时为0,按理说这是没意义的,但这种情况确实存在,故我们要预防这种情况,添加一个if判断即可:

void insert(size_t pos, const char* str)
{assert(pos < _size);size_t len = strlen(str);if (len == 0)  //这里判断一下特殊的情况return;if (_size + len > _capacity){//扩容//大于2倍,需要多少开多少,不足2倍,按二倍扩reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}size_t end = _size + 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;
}
(9)erase()
//从pos位置开始,向后删除len个字符
void erase(size_t pos, size_t len)
{assert(pos < _size); if (len > _size - pos) //删除pos往后的所有字符{_str[pos] = '\0';_size = pos;}else{for (size_t i = pos + len;i <= _size; ++i){_str[i - len] = _str[i];}_size -= len;}
}

在主函数中调用test_string6():

void test_string6()
{string s("hello world");cout << s.c_str() << endl;s.erase(0, 6);cout << s.c_str() << endl;
}

运行结果:

(10)find()
//从pos位置向后查找,找到第一个字符ch返回其下标,否则返回npos
size_t find(char ch, size_t pos)
{assert(pos < _size);for (size_t i = pos;i < _size;i++){if (_str[i] == ch)return i;}return npos; //static const size_t npos = -1
}

 下面是上面的一个重载函数:

//从pos位置向后查找,找到第一个字串str返回其首个字符的下标,否则返回npos
size_t find(const char* str, size_t pos)
{assert(pos < _size);const char* ptr = strstr(_str + pos, str); //如果找到字串,返回母串中子串的起始位置的指针if (ptr== nullptr){return npos;}else{return ptr - _str;}
}
(11)substr() 
//取子串
string substr(size_t pos, size_t len)
{assert(pos < _size);//len大于剩余字符长度,更新一下lenif (len > _size - pos){len = _size - pos;}string sub; //用来存放字串sub.reserve(len); //提前预留出空间,避免频繁扩容for (size_t i = 0; i < len; ++i){sub += _str[pos + i];}return sub;
}

 在主函数中调用test_string7():

void test_string7()
{string s("Test.txt");size_t pos = s.find('.');string tmp = s.substr(pos,npos);  //static const size_t npos = -1;cout << tmp.c_str() << endl;
}

运行结果:

程序崩溃,打印了一串乱码,肯定是我们的程序写的有点问题。

从代码中看,因为不能写传引用返回,所以我们写的是传值拷贝,传值返回会调用拷贝构造,我们此刻没有写拷贝构造,所以会调用默认生成的拷贝构造完成浅拷贝,问题就出在这个浅拷贝身上,如果是浅拷贝,调用结束后sub指向的空间就被收回了,而由于是浅拷贝tmp的_str又指向被收回的空间,所以会造成非法访问内存空间。导致打印一串乱码,程序崩溃。

所以我们要想解决这个问题就需要单独写一个拷贝构造,来进行深拷贝。

//拷贝构造 -- 深拷贝
string(const string& str)
{_str = new char[str._capacity + 1];strcpy(_str, str._str);_size = str._size;_capacity = str._capacity;
}

再次运行: 

这次代码正常运行。

六、非成员函数

(1)重载关系运算符
bool operator<(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) < 0;
}	
bool operator==(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) == 0;
}
bool operator<=(const string& s1, const string& s2)
{return s1 < s2 || s1 == s2;
}
bool operator>(const string& s1, const string& s2)
{return !(s1 <= s2);
}
bool operator>=(const string& s1, const string& s2)
{return !(s1 < s2);
}bool operator!=(const string& s1, const string& s2)
{return !(s1 == s2);
}

我们只需重载<和==,其余运算符重载直接复用它们两个即可实现。 

在主函数中调用test_string8():

void test_string8()
{string s1("Hello");string s2("Hello");cout << (s1 == s2) << endl;cout << (s1 > s2) << endl;cout << (s1 < s2) << endl;cout << (s1 == "Hello") << endl; //隐式类型转换cout << ("world" == "Hello") << endl; //重载的条件必须满足至少一个是自定义类型,这里是两个const类型的char指针在比较
}

运行结果:

 

(2) 重载流插入(<<)和流提取(>>)
ostream& operator<<(ostream& out, const string& str)
{for (auto e : str){out << e;}return out;
}
istream& operator>>(istream& in, string& str)
{char ch;in >> ch;while (ch != ' ' && ch != '\n'){str += ch;in >> ch;}return in;
}

在主函数中调用test_string9():

void test_string9()
{string s1;cin >> s1;cout << s1;
}

运行结果: 

 

我们发现遇到空格或换行,程序并没有终止,也就是cin还在进行,初步判断问题出在重载流提取>>时出现了错误。

原因是cin在控制台拿数据时,会将空格和换行认为是分隔符,会读空格和换行但仅仅认为它们是分隔符,也就是取不到空格和换行。所以,界面就会一直等待输出,循环出不来。换成get可以解决问题,get是什么字符都可以取到。在C语言中scanf和cin一样,也是取不到空格和换行,可以换成getchar来解决问题。

修改后代码:

istream& operator>>(istream& in, string& str)
{char ch;ch = in.get(); //调用get是什么字符就接收什么字符,不会过滤掉空格while (ch != ' ' && ch != '\n'){str += ch;ch = in.get();}return in;
}

在主函数中调用test_string10(): 

void test_string10()
{string s1("hello world");cin >> s1;cout << s1;
}

运行结果: 

不对呀,这结果怎么是这样的?

因为s1是有内容的,所以在调用流提取>>时,要先将对象中的内容清空,代码如下:

void clear()
{_str[0] = '\0';_size = 0;
}istream& operator>>(istream& in, string& str)
{str.clear();char ch;ch = in.get(); //调用get是什么字符就接收什么字符,不会过滤掉空格while (ch != ' ' && ch != '\n'){str += ch;ch = in.get();}return in;
}

运行结果: 

这样就完成了重载流提取>>的工作。 

优化流提取>>:

调用>>,当我们插入大量字符时,会不断的进行+=,会造成频繁扩容,会损耗性能。

这里进行优化:

istream& operator>>(istream& in, string& str)
{str.clear();const int N = 256;char buff[N]; //作为缓冲int i = 0;char ch;ch = in.get(); //调用get是什么字符就接收什么字符,不会过滤掉空格while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';str += buff;i = 0;}ch = in.get();}if (i > 0) //没进if语句{buff[i] = '\0';str += buff;}return in;
}

这个代码的好处是:如果输入很长的字符串不会频繁扩容,如果输入很短的字符串,影响也不大,因为我的buff是在栈中开辟的,调用完就销毁了,对程序的性能影响不大。 

 (3)getline()

getline的功能和流提取>>差不多,区别是getline遇到空格不结束,遇到换行才结束。

我们稍作修改即可实现:

istream& getlien(istream& in, string& str)
{str.clear();char ch;ch = in.get(); //调用get是什么字符就接收什么字符,不会过滤掉空格while (ch != '\n'){str += ch;ch = in.get();}return in;
}

 七、源码

(1)string.h
#pragma once
#include <string>
#include <assert.h>
#include <iostream>
using namespace std;namespace blue
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}iterator begin() const{return _str;}iterator end() const{return _str + _size;}typedef const char* const_iterator;const_iterator cbegin() const{return _str;}const_iterator cend() const{return _str + _size;}//短小频繁调用的函数可以直接定义到类中,默认是inlinestring(const char* str = ""){_size = strlen(str);_capacity = _size; //_capacity不包含'\0',所以不用+1_str = new char[_capacity + 1]; //+1是为了存放'\0'strcpy(_str, str);}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}string(const string& str){_str = new char[str._capacity + 1];strcpy(_str, str._str);_size = str._size;_capacity = str._capacity;}string& operator=(const string& str){if (this != &str){delete[] _str; //先释放旧空间,否则会造成内存泄漏_str = new char[str._size + 1];strcpy(_str, str._str);_size = str._size;_capacity = str._capacity;}return *this;}const char* c_str() const{return _str;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}char& operator[](size_t pos){assert(pos >= 0 && pos < _size); //越界直接报错return _str[pos];}const char& operator[](size_t pos) const{assert(pos >= 0 && pos < _size); //越界直接报错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 find(const char* str, size_t pos = 0);string substr(size_t pos = 0, size_t len = npos);private:char* _str;size_t _size;size_t _capacity;//static const size_t npos = -1;//static const double d = 1.1;//errstatic const size_t npos;};bool operator<(const string& s1, const string& s2);bool operator==(const string& s1, const string& s2);bool operator<=(const string& s1, const string& s2);bool operator>(const string& s1, const string& s2);bool operator>=(const string& s1, const string& s2);bool operator!=(const string& s1, const string& s2);ostream& operator<<(ostream& out, const string& str);istream& operator>>(istream& in, string& str);}
(2)string.cpp
#include "string.h"namespace blue
{const size_t string::npos = -1;//设置容量void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];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++;_str[_size] = '\0';}//在字符串最后位置上追加一个字符串void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){//扩容//大于2倍,需要多少开多少,不足2倍,按二倍扩reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}strcpy(_str + _size, str);_size += len;}//在字符串最后位置上接一个字符string& string::operator+=(char ch){push_back(ch);return *this;}//在字符串最后位置上接一个字符串string& string::operator+=(const char* str){append(str);return *this;}//在pos位置前插入一个字符void string::insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){//扩容reserve(_capacity * 2 == 0 ? 4 : _capacity * 2);}//挪动数据//int end = _size; //必须将end类型改为int//while (end >= (int)pos)  //比较时必须将pos类型强制转换为int//{//	_str[end + 1] = _str[end];//	--end;//}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 (len == 0)  //这里判断一下特殊的情况return;if (_size + len > _capacity){//扩容//大于2倍,需要多少开多少,不足2倍,按二倍扩reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}size_t end = _size + 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;}//从pos位置开始,向后删除len个字符void string::erase(size_t pos, size_t len){assert(pos < _size);if (len > _size - pos){_str[pos] = '\0';_size = pos;}else{for (size_t i = pos + len;i <= _size; ++i){_str[i - len] = _str[i];}_size -= len;}}//从pos位置向后查找,找到第一个ch返回其下标,否则返回npossize_t string::find(char ch, size_t pos){assert(pos < _size);for (size_t i = pos;i < _size;i++){if (_str[i] == ch)return i;}return npos; //static const size_t npos = -1}size_t string::find(const char* str, size_t pos){assert(pos < _size);const char* ptr = strstr(_str + pos, str); //如果找到字串,返回母串中子串的起始位置的指针if (ptr == nullptr){return npos;}else{return ptr - _str;}}//取子串string string::substr(size_t pos, size_t len){assert(pos < _size);//len大于剩余字符长度,更新一下lenif (len > _size - pos){len = _size - pos;}string sub; //用来存放字串sub.reserve(len); //提前预留出空间,避免频繁扩容for (size_t i = 0; i < len; ++i){sub += _str[pos + i];}return sub;}bool operator<(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) < 0;}	bool operator==(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str()) == 0;}bool operator<=(const string& s1, const string& s2){return s1 < s2 || s1 == s2;}bool operator>(const string& s1, const string& s2){return !(s1 <= s2);}bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}ostream& operator<<(ostream& out, const string& str){for (auto e : str){out << e;}return out;}//istream& operator>>(istream& in, string& str)//{//	str.clear();//	char ch;//	//in >> ch;//	ch = in.get(); //调用get是什么字符就接收什么字符,不会过滤掉空格//	while (ch != ' ' && ch != '\n')//	{//		str += ch;//		//in >> ch;//		ch = in.get();//	}//	return in;//}istream& operator>>(istream& in, string& str){str.clear();const int N = 256;char buff[N]; //作为缓冲int i = 0;char ch;ch = in.get(); //调用get是什么字符就接收什么字符,不会过滤掉空格while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';str += buff;i = 0;}ch = in.get();}if (i > 0) //没进if语句{buff[i] = '\0';str += buff;}return in;}
}
(3)Test.cpp 
#include "string.h"namespace blue
{void test_string1(){//string s1;//string s2("hello world");//cout << s1.c_str() << endl;//cout << s2.c_str() << endl;string s3("good morning");string::iterator it = s3.begin();while (it != s3.end()){cout << *it << " ";++it;}cout << endl;}void test_string2(){string s1("good morning");string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;}void test_string3(){string s1("hello world");s1.push_back('x');s1.push_back('x');cout << s1.c_str() << endl;}void test_string4(){string s1("hello world");s1 += 'x';s1 += 'x';cout << s1.c_str() << endl;}void test_string5(){string s("hello world");cout << s.c_str() << endl;s.insert(0, 'x');cout << s.c_str() << endl;}void test_string6(){string s("hello world");cout << s.c_str() << endl;s.erase(0, 6);cout << s.c_str() << endl;}void test_string7(){string s("Test.txt");size_t pos = s.find('.');string sub = s.substr(pos); cout << sub.c_str() << endl;}void test_string8(){string s1("Hello");string s2("Hello");cout << (s1 == s2) << endl;cout << (s1 > s2) << endl;cout << (s1 < s2) << endl;cout << (s1 == "Hello") << endl; //隐式类型转换cout << ("world" == "Hello") << endl; //重载的条件必须满足至少一个是自定义类型,这里是两个const类型的char指针在比较}void test_string9(){string s1;cin >> s1;cout << s1;}void test_string10(){string s1("hello world");cin >> s1;cout << s1;}
}int main()
{//blue::test_string1();//blue::test_string2();//blue::test_string3();//blue::test_string4();//blue::test_string5();//blue::test_string6();//blue::test_string7();//blue::test_string8();//blue::test_string9();blue::test_string10();return 0;
}

在外面套了一个命名空间blue是为了防止与C++标准库中string发生冲突。 

八、总结

本篇内容到这里就结束了,主要讲了string的模拟实现的大致思路,希望帮助到大家,祝大家天天开心!

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • LabVIEW重构其他语言开发的旧系统
  • 个人hic分析流程搭建4—compartment模块分析
  • 切线空间:unity中shader切线空间,切线矩阵,TBN矩阵 ,法线贴图深度剖析
  • 基于人工智能的自动驾驶系统项目教学指南
  • 三星ZFlip5/ZFlip4/W7024刷安卓14国行OneUI6.1系统-高级设置-韩/欧/港版
  • StreamReader 和 StreamWriter提供自动处理字符编码的功能
  • AI+摄影:行业变革与创新机遇
  • 建筑工程资料保护策略:打造安全的建筑文档管理方案
  • 【数据结构精讲】01绪论(基本概念介绍和时间复杂度计算)
  • 【Android安全】Ubuntu 16.04安装GDB和GEF
  • 机器学习之实战篇——MNIST手写数字0~9识别(全连接神经网络模型)
  • AI与艺术的碰撞:当机器开始创作,创造力何在?
  • 前端性能优化——对节流与防抖的理解
  • CSS基本布局理解——WEB开发系列38
  • JAVA-网络(0907)
  • [iOS]Core Data浅析一 -- 启用Core Data
  • javascript 总结(常用工具类的封装)
  • Java-详解HashMap
  • Java知识点总结(JavaIO-打印流)
  • Linux各目录及每个目录的详细介绍
  • Lsb图片隐写
  • Python - 闭包Closure
  • Python爬虫--- 1.3 BS4库的解析器
  • Vue 2.3、2.4 知识点小结
  • 阿里研究院入选中国企业智库系统影响力榜
  • 思维导图—你不知道的JavaScript中卷
  • 算法---两个栈实现一个队列
  • 走向全栈之MongoDB的使用
  • 06-01 点餐小程序前台界面搭建
  • 说说我为什么看好Spring Cloud Alibaba
  • ​Java基础复习笔记 第16章:网络编程
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • %check_box% in rails :coditions={:has_many , :through}
  • (20)docke容器
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (javascript)再说document.body.scrollTop的使用问题
  • (多级缓存)多级缓存
  • (二)Kafka离线安装 - Zookeeper下载及安装
  • (四十一)大数据实战——spark的yarn模式生产环境部署
  • (一)Java算法:二分查找
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)大型网站架构演变和知识体系
  • (自用)网络编程
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)
  • .bat批处理(八):各种形式的变量%0、%i、%%i、var、%var%、!var!的含义和区别
  • .Net 6.0--通用帮助类--FileHelper
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET Framework杂记
  • .Net IOC框架入门之一 Unity
  • .Net6 Api Swagger配置
  • /deep/和 >>>以及 ::v-deep 三者的区别
  • @entity 不限字节长度的类型_一文读懂Redis常见对象类型的底层数据结构
  • @RequestParam @RequestBody @PathVariable 等参数绑定注解详解
  • [30期] 我的学习方法