目录
一、我们先创建三个文件分别为 String.h(声明)、String.cpp(定义)、teat.cpp(测试)
二、成员函数
构造函数与析构函数
🌟string()
🌟string(const char* str)
🌟~string()
🌟深拷贝string(const string& s)
遍历字符串
🌟operator[ ]
🌟iterator 迭代器
对内容的修改
🌟reserve 开空间
🌟push_back 尾插
🌟append 尾插字符串
🌟operator+= 字符串追加
🌟insert 在指定位置插入字符或字符串
🌟erase 删除
🌟find 查找字符或字符串
🌟substr 在str中从pos位置开始,截取n个字符,然后将其返回
几个重载函数
🌟operator= 赋值重载
🌟operator== / < 等几个比较函数
三、非成员函数
🌟流插入和流提取
四、完整代码
我们实现的是basic_string:
一、我们先创建三个文件分别为 String.h(声明)、String.cpp(定义)、teat.cpp(测试)
二、成员函数
构造函数与析构函数
🌟string()
空字符串构造:
String::String()
{
_str = new char[1] {'\0'};
_size = 0;
_capacity = 0;
}
这里我们给字符数组分配一个空间大小来储存\0 ,同时设置类的_size和_capacity成员变量的初始值。
一些细节:
以上涉及到相关的接口实现往接着下看😊
🌟string(const char* str)
用常量字符串来实例化一个对象:
String::String(const char* str)
:_str(new char[strlen(str) + 1])//+1给末尾存储\0
, _size(strlen(str))
, _capacity(strlen(str))
{
strcpy(_str, str);//用字符串函数将常量字符串拷贝到对象的数组中
}
我们可以对代码进行优化,可以将上述两个函数通过缺省值合并为一个函数 。
String::String(const char* str="")
:_size(strlen(str))
{
_str = new char[_size + 1];// +1是给末尾存储\0
_capacity = _size;
strcpy(_str, str); //用字符串函数将常量字符串拷贝到对象的数值中
}
缺省值即为空字符串,初始化列表只为size赋值减少对strlen的多次调用。
一些细节:
全缺省参数该怎样声明呢?
🌟~string()
析构函数
String::~String()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
🌟深拷贝string(const string& s)
系统默认的拷贝是浅拷贝,对象拷贝的时候是按字节一个一个拷贝,对内置类型默认生成的拷贝构造就是完成浅拷贝/值拷贝,会导致两个字符串同时指向同一个空间,在析构时可能会造成多次释放使系统崩溃。
因此需要手动实现深拷贝:
String::String(const String& s)
{
_str = new char[s._capacity + 1];//开新的空间
strcpy(_str, s._str);//把旧空间的内容拷贝到新的空间
_size = s._size;
_capacity = s._capacity;
}
遍历字符串
首先我们用三个函数来获取三个成员变量:
我们这里设置为const成员函数,使String类对象和const String类对象都可以调用这几个函数。
const char* String::c_str() const
{
return _str;
}
size_t String::size() const
{
return _size;
}
size_t String::capacity() const
{
return _capacity;
}
🌟operator[ ]
<分为两种>
第一种:允许对字符串内容进行修改
char& String::operator[](size_t pos)
{
assert(pos < _size);//string会抛异常
return _str[pos];//返回值为 char*
}
第二种:不能修改类的成员变量
const char& String::operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];//返回值为 const char*
}
🌟iterator 迭代器
<分为两种>
非常量:
//String.h
typedef char* iterator;
iterator begin();
iterator end();
//String.cpp
String::iterator String::begin()// char*
{
return _str;
}
String::iterator String::end()
{
return _str + _size;
}
常量: 不是本身不可以修改,要保证的是指向的内容不能修改。
//String.h
typedef const char* const_iterator;
const_iterator begin() const;
const_iterator end() const;
//String.cpp
String::const_iterator String::begin() const//const char*
{
return _str;
}
String::const_iterator String::end() const
{
return _str + _size;
}
测试:
// 封装:屏蔽了底层实现细节,
// 提供了一种简单通用访问容器的方式
xlf::String s1("hello world");
xlf::String::iterator it1 = s1.begin();
while (it1 != s1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
for (auto e : s1)
{
cout << e << " ";
}
cout << endl;
const xlf::String s2("xxxxxxx");
xlf::String::const_iterator it2 = s2.begin();
while (it2 != s2.end())
{
// *it2 = 'y';//本身不能修改
cout << *it2 << " ";
++it2;
}
cout << endl;
for (size_t i = 0; i < s2.size(); i++)
{
//s2[i]++;//本身不能修改
cout << s2[i] << " ";//只能访问
}
cout << endl;
对内容的修改
🌟reserve 开空间
void String::reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];//开一个更大的空间 +1存储\0
strcpy(tmp, _str);//拷贝旧空间的内容,到新空间
delete[] _str;//先释放旧空间
_str = tmp;//指向新空间
_capacity = n;//能存储有效字符个数的空间
}
}
🌟push_back 尾插
void String::push_back(char ch)
{
if (_size == _capacity)//先判断空间的大小
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
_str[_size + 1] = '\0';//末尾填上\0
++_size;
}
🌟append 尾插字符串
//"hello" "xxxxxxxxxxxxx"
//append不能只扩容两倍
void String::append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + len, str);//可直接找的\0,直接复制
_size += len;
}
🌟operator+= 字符串追加
//非常量
String& String::operator+=(char ch)
{
push_back(ch);
return *this;
}
//常量
String& String::operator+=(const char* str)
{
append(str);
return *this;
}
重载运算符的返回值:
内置类型用这个运算符的返回值是什么
自定义类型用这个运算符的返回值是什么
例:
int i ;
i+= 的返回值是 i
🌟insert 在指定位置插入字符或字符串
在讲insert之前我们先讲一个小知识:
我们知道静态成员变量,是在类内声明,在类外定义的
补充小知识:
//插入一个字符
void String::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
size_t end = _size;//size初始指向末尾\0位置,将数据不断往后挪,直到腾出pos位置
while (end >= pos)//当pos位置为0时,end-会变成-1,
//而无符号整型-1是一个很大的值,导致无法跳出循环
{
_str[end + 1] = _str[end];
--end;
}
_str[pos] = ch;
++_size;
}
改进:
void String::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
size_t end = _size + 1;//end初始指向\0的下一个位置
while (end > pos)//当end=pos时,跳出循环
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
++_size;
}
//插入一个字符串
void String::insert(size_t pos, const char* str)
{
assert(pos <= _size);
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;
}
🌟erase 删除
void String::erase(size_t pos, size_t len)
{
assert(pos < _size);
//len大于前面字符个数时,有多少删多少
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else//往前覆盖
{
// 目标 源
strcpy(_str + pos, _str + pos + len);//将源字符串复制到目标字符串,包括\0
//将pos+len的字符串覆盖到pos位置上
_size -= len;
}
}
🌟find 查找字符或字符串
//查找字符
size_t string::find(char ch, size_t pos)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
//查找字符串
size_t string::find(const char* sub, size_t pos)
{
char* p = strstr(_str + pos, sub);//返回字符串中首次出现子串的地址
return p - _str;
}
🌟substr 在str中从pos位置开始,截取n个字符,然后将其返回
string string::substr(size_t pos, size_t len)
{
// len大于后面剩余字符,有多少取多少
if (len > _size - pos)
{
string sub(_str + pos);//直接构造
return sub;
}
else
{
string sub;
sub.reserve(len);
for (size_t i = 0; i < len; i++)
{
sub += _str[pos + i];
}
return sub;
}
}
几个重载函数
🌟operator= 赋值重载
String& String::operator=(const String& s)
{
if (this != &s)
{
char* tmp = new char[s._capacity + 1];//开一个新的空间(多开一个给\0)
strcpy(tmp, s._str);//把s._str的内容拷贝到新开的空间
delete[] _str;//释放旧空间
_str = tmp;//指向新开的空间
_size = s._size;//更改指向
_capacity = s._capacity;//更改指向
}
return *this;//连续赋值
}
🌟operator== / < 等几个比较函数
复用:
bool String::operator<(const String& s) const
{
return strcmp(_str, s._str) < 0;//两个字符串进行比较
}
bool String::operator>(const String& s) const
{
return !(*this <= s);
}
bool String::operator<=(const String& s) const
{
return *this < s || *this == s;
}
bool String::operator>=(const String& s) const
{
return !(*this < s);
}
bool String::operator==(const String& s) const
{
return strcmp(_str, s._str) == 0;
}
bool String::operator!=(const String& s) const
{
return !(*this == s);
}
三、非成员函数
🌟流插入和流提取
输入是对内容的覆盖,所以我们先实现一个clear()函数来清空字符串
void String::clear()
{
_str[0] = '\0';
_size = 0;
}
流插入:
istream& operator>>(istream& is, String& str)
{
str.clear();
char ch = is.get();
while (ch != ' ' && ch != '\n')
{
str += ch;//连续不断的+=会进行不断地扩容
ch = is.get();
}
return is;
}
改进:
istream& operator>>(istream& is, string& str)
{
str.clear();
char buff[128];//缓冲数组
int i = 0;
char ch = is.get();
while (ch != ' ' && ch != '\n')//读到空格、换行退出
{
buff[i++] = ch;
//0 - 126
if (i == 127)
{
buff[i] = '\0';
str += buff;
i = 0;
}
ch = is.get();
}
if (i != 0)
{
buff[i] = '\0';
str += buff;
}
return is;
}
流提取:
ostream& operator<<(ostream& os, const String& str)
{
for (size_t i = 0; i < str.size(); i++)
{
os << str[i];
}
return os;
}
四、完整代码
//string.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace xlf
{
class String
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
//String();
String(const char* str = "");
String(const String& s);
String& operator=(const String& s);
~String();
const char* c_str() const;
size_t size() const;
char& operator[](size_t pos);
const char& operator[](size_t pos) const;
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 = 0, size_t len = npos);
size_t find(char ch, size_t pos = 0);
size_t find(const char* str, size_t pos = 0);
void swap(String& s);
String substr(size_t pos = 0, size_t len = npos);
String& operator=(const String& s);
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;
void clear();
private:
char* _str;
size_t _size;
size_t _capacity;
const static size_t npos;
};
istream& operator>>(istream& is, String& str);
ostream& operator<<(ostream& os, const String& str);
}
string.cpp
#include"string.h"
namespace xlf
{
const size_t String::npos = -1;//要指定类域
String::iterator String::begin()// char*
{
return _str;
}
String::iterator String::end()
{
return _str + _size;
}
String::const_iterator String::begin() const//const char*
{
return _str;
}
String::const_iterator String::end() const
{
return _str + _size;
}
String::String(const char* str)
:_size(strlen(str))
{
_str = new char[_size + 1];
_capacity = _size;
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)
{
char* tmp = new char[s._capacity + 1];//开新的空间
strcpy(tmp, s._str);//把旧空间的内容拷贝到新的空间
delete[] _str;//释放旧空间
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
String::~String()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
const char* String::c_str() const
{
return _str;
}
size_t String::size() const
{
return _size;
}
char& String::operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& String::operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
void String::reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];//开一个更大的空间 +1存储\0
strcpy(tmp, _str);//拷贝旧空间的内容,到新空间
delete[] _str;//先释放旧空间
_str = tmp;//指向新空间
_capacity = n;//能存储有效字符个数的空间
}
}
void String::push_back(char ch)
{
if (_size == _capacity)//先判断空间的大小
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
_str[_size + 1] = '\0';//末尾填上\0
++_size;
}
//"hello" "xxxxxxxxxxxxx"
//append不能只扩容两倍
void String::append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + len, str);//可直接找的\0,直接复制
_size += len;
}
String& String::operator+=(char ch)
{
push_back(ch);
return *this;
}
String& String::operator+=(const char* str)
{
append(str);
return *this;
}
void String::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
size_t end = _size + 1;//end初始指向\0的下一个位置
while (end > pos)//当end=pos时,跳出循环
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
++_size;
}
void String::insert(size_t pos, const char* str)
{
assert(pos <= _size);
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;
}
void String::erase(size_t pos, size_t len)
{
assert(pos < _size);
//len大于前面字符个数时,有多少删多少
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else//往前覆盖
{
// 目标 源
strcpy(_str + pos, _str + pos + len);//将源字符串复制到目标字符串,包括\0
_size -= len;
}
}
size_t String::find(char ch, size_t pos)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t String::find(const char* str, size_t pos)
{
char* p = strstr(_str + pos, str);//返回字符串中首次出现子串的地址
return p - _str;
}
void String::swap(String& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
String String::substr(size_t pos, size_t len)
{
//len大于后面剩余字符,有多少取多少
if (len > _size - pos)
{
String sub(_str + pos);
return sub;
}
else
{
String sub;
sub.reserve(len);
for (size_t i = 0; i < len; i++)
{
sub += _str[pos + i];
}
return sub;
}
}
String& String::operator=(const String& s)
{
if (this != &s)
{
char* tmp = new char[s._capacity + 1];//开一个新的空间(多开一个给\0)
strcpy(tmp, s._str);//把s._str的内容拷贝到新开的空间
delete[] _str;//释放旧空间
_str = tmp;//指向新开的空间
_size = s._size;//更改指向
_capacity = s._capacity;//更改指向
}
return *this;//连续赋值
}
bool String::operator<(const String& s) const
{
return strcmp(_str, s._str) < 0;//两个字符串进行比较
}
bool String::operator>(const String& s) const
{
return !(*this <= s);
}
bool String::operator<=(const String& s) const
{
return *this < s || *this == s;
}
bool String::operator>=(const String& s) const
{
return !(*this < s);
}
bool String::operator==(const String& s) const
{
return strcmp(_str, s._str) == 0;
}
bool String::operator!=(const String& s) const
{
return !(*this == s);
}
void String::clear()
{
_str[0] = '\0';
_size = 0;
}
istream& operator>>(istream& is, String& str)
{
str.clear();
char buff[128];//缓冲数组
int i = 0;
char ch = is.get();
while (ch != ' ' && ch != '\n')//读到空格、换行退出
{
buff[i++] = ch;
//0 - 126
if (i == 127)
{
buff[i] = '\0';
str += buff;
i = 0;
}
ch = is.get();
}
if (i != 0)
{
buff[i] = '\0';
str += buff;
}
return is;
}
ostream& operator<<(ostream& os, const String& str)
{
for (size_t i = 0; i < str.size(); i++)
{
os << str[i];
}
return os;
}
}
如若对你有帮助,记得点赞、收藏、关注哦!
若有误,望各位,在评论区留言或者私信我 指点迷津!!!谢谢^ ^ ~