目录
一、我们先创建三个文件分别为 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.htypedef char* iterator;iterator begin();iterator end();//String.cppString::iterator String::begin()// char* {return _str;}String::iterator String::end(){return _str + _size;}
常量: 不是本身不可以修改,要保证的是指向的内容不能修改。
//String.htypedef const char* const_iterator;const_iterator begin() const;const_iterator end() const;//String.cppString::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存储\0strcpy(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 - 126if (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存储\0strcpy(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 - 126if (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;}}
如若对你有帮助,记得点赞、收藏、关注哦!
若有误,望各位,在评论区留言或者私信我 指点迷津!!!谢谢^ ^ ~