文章目录

  • 前言:
  • string类的初识
  • 命名空间
  • 数据成员
  • 构造函数
    • 自定义构造函数
    • 拷贝构造函数
    • 赋值运算符
  • 析构函数
  • c_str()
  • clear()
  • size()
  • 操作符[]
  • 迭代器
  • 扩容函数
    • reserve()
    • resize()
  • 插入
    • 尾插
      • push_back()
      • append()
      • 操作符+=
    • 头插
      • insert()
    • find()
    • erase()
  • 输入输出函数
    • operator<<()
    • operator>>()
  • 比较函数
    • operator==()
    • operator!=()
    • operator<=()
    • operator>()
    • operator>=()
  • 全部代码
    • String.h
    • String.cpp
  • 总结

前言:

  • 本文介绍STL-string中常用函数的复写

  • 博主收集的资料New Young,连载中。

  • 博主收录的问题:New Young

  • 转载请标明出处:New Young

string类的初识

  1. string本质是basic_string模板类的实例化后的重命名,其中含有很多处理字符串的成员函数:重栽的[],=等
typedef basic_string<char> string;

命名空间

在复写string类时,因为string类是标准库中的内置类型,为了避免命名污染问题,可以命名一个自己的命名空间

//完成string的增删查改复写
namespace My_Str //防止污染std命名空间
{class string{private:char* _str;int _size;//有效字符的个数int _capacity;//有效字符的容量,不包含'\0',但是因为开辟空间时多开辟一个,//因此默认预留了一个'\0',方便执行C函数......};

数据成员

这里使用数组形式,方便操作

namespace My_Str //防止污染std命名空间
{class string{private:char* _str;int _size;//有效字符的个数int _capacity;//有效字符的容量,不包含'\0',但是因为开辟空间时多开辟一个,//因此默认预留了一个'\0',方便执行C函数

构造函数

  1. string标准在初始化是不能用单一字符的,只能用字符串或者已存在的对象。

  2. 在这些构造函数中推荐使用现代写法,利用了函数栈帧后局部变量自动销毁和对代码的灵活复用

自定义构造函数

  1. 全缺省的构造函数,对于空字符串,_size为0,在扩容时要留意
//全缺省的构造函数,对于空字符串,_size为0,在扩容时要留意
//string(const char* str="");
My_Str::string::string(const char* str):_size(strlen(str)), _capacity( _size)
{_str = new char[_capacity+1];//+1是给\0预留空间,防止strcpy(_str, str);
}

拷贝构造函数

  1. 在不用引用的情况下, 实参到形参的传递也是一种拷贝
  2. 在有需要动态管理的成员时,防止浅拷贝造成的对同一空间多次delete问题
  3. 现代写法,本质是对代码的复用和对局部变量函数栈帧后销毁的操作
//拷贝构造,注意
// 1. 在不用引用的情况下, 实参到形参的传递也是一种拷贝
// 2.在有需要动态管理的成员时,防止浅拷贝造成的对同一空间多次delete问题
My_Str::string::string(const string& s){一般写法//_str = new char[strlen(s._str) + 1];//strcpy(_str, s._str);//_size = s._size;//_capacity = s._capacity;现代写法,本质是对代码的复用和对局部变量函数栈帧后销毁的操作string tmp(s._str);//调用了自定义的构造函数_str = nullptr;//这步必须要有,否则就是对随机值进行deleteswap(tmp);//string下的swap函数//std::swap(_str,tmp._str);//标准库下的swap模板函数}

赋值运算符

  1. 一般需要考虑自身赋值问题

  2. 现代写法,因为拷贝构造后的对象地址不同,因此不用考虑自身赋值和多次释放同一空间的问题

My_Str::string& My_Str::string::operator=(const My_Str::string& s)
{//自身的赋值//if (&s != this)//{// //一般写法://    char *tmp = new char[s._size + 1];//防止申请失败,C++的new会抛异常,未学//   strcpy(tmp, s._str);//  delete[]_str;//释放原先的空间//    _str = tmp;//局部变量销毁后,堆的空间仍存在。// _size = s._size;// _capacity = s._capacity;//}//现代写法,不用考虑自身赋值和多次释放同一空间的问题string tmp(s);//std::swap(_str, tmp._str);_size = tmp._size;_capacity = tmp._capacity;return *this;}

析构函数

My_Str::string::~string()//析构函数
{delete[]_str;_str = nullptr;_size =_capacity= 0;}

c_str()

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

clear()

void My_Str::string::clear()
{_size = 0;
}

size()

size_t My_Str::string::size()
{return _size;
}size_t My_Str::string::size()const
{return _size;
}

操作符[]

char& My_Str::string::operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}const char& My_Str::string::operator[](size_t pos)const
{assert(pos < _size);return _str[pos];
}

迭代器

迭代器,暂时理解为指针

typedef char* iterator;
typedef const char* const_iterator;My_Str::string::iterator My_Str::string::begin()
{return _str;
}
My_Str::string::iterator My_Str::string::end()
{return _str + _size ;}My_Str::string::const_iterator My_Str::string::begin()const
{return _str;
}
My_Str::string::const_iterator My_Str::string::end()const
{return _str + _size;}

扩容函数

reserve()

不支持缩小扩,因此只需要考虑n>_capacity的情况

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

resize()

支持缩小扩,因此需要以size为界,考虑情况

void  My_Str::string::resize(size_t n, char ch)
{//resize不支持缩小_capacity;if (n <= _size){_str[n] = '\0';_size = n;}else {if (n > _capacity){reserve(n);}memset(_str + _size, ch, n - _size);_str[_size] = '\0';}
}

插入

尾插

  1. 单个字符,只需要按情况扩容2倍即可,
  2. 但是对于字符串,其个数不可空,因此扩容时依情况确定。
  3. 对于尾插更建议使用 +=;

push_back()

void My_Str::string::push_back(char ch)
{if (_size == _capacity){reserve(_capacity==0?4:_capacity*2);}_str[_size] = ch;++_size;_str[_size] = '\0';
}

append()

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

操作符+=

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

头插

insert()

头插,一般不建议使用,效率低

My_Str::string& My_Str::string::insert(size_t pos, const char ch)
{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//因为是无符号整型,在减运算中要注意死循环问题size_t end = _size+1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;return *this;
}
My_Str::string& My_Str::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;}strncpy(_str + pos, str,len);//strcpy会将'\0添加进去'return *this;
}

find()

size_t My_Str::string::find(char ch)
{for (size_t i = 0; i < _size; ++i){if (ch == _str[i]){return i;}}return npos;
}
size_t My_Str::string::find(const char* str, size_t pos)
{assert(pos < _size&& strlen(str)<_size);const char* ret = strstr(_str, str);if (ret != nullptr){return ret - _str;}return npos;
}

erase()

My_Str::string& My_Str::string::erase(size_t pos , size_t len )
{assert(pos < _size);if (len == npos || len >= (_size - pos))//删除全部{_str[pos] = '\0';_size = pos;}else {strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;
}

输入输出函数

operator<<()

std::ostream& operator<<(std::ostream& out, const My_Str::string& s)
{for (int i = 0; i < s.size(); ++i)//有限次的循环,可以避免字符串中间含有'\0'的情况{  out << s[i];}//cout<<s.c_str();return out;
}

operator>>()

std::istream& operator >> (std::istream& in,  My_Str::string& s)
{char ch = 0;//ch = getchar();//while (ch != ' '&&ch!='\n')//{//    s += ch;//    ch = getchar();//}s.clear();ch = in.get();while (ch != ' '&&ch!='\n'){s += ch;ch = in.get();}return in;
}

比较函数

因为string的接口很多,这里将重载的函数都定义为全局函数

operator<()

//"abcd" "abcd" false
//"abcd" "abcde"true
//"abcde" "abcd"false
bool operator<(const My_Str::string& s1, const My_Str::string& s2)
{size_t i1 = 0;size_t i2 = 0;while ( i1< s1.size()&& i2<s2.size()){if (s1[i1] < s2[i2]){return false;}++i1;++i2;}return i1 < s1.size() ? false : true;}

operator==()

bool operator==(const My_Str::string& s1, const My_Str::string& s2)
{size_t i1 = 0;size_t i2 = 0;while (i1 < s1.size() && i2 < s2.size()){if (s1[i1] != s2[i2]){return false;}++i1;++i2;}return i1 == s1.size() && i2 == s2.size() ? true : false;
}

operator!=()

bool operator!=(const My_Str::string& s1, const My_Str::string& s2)
{return !(s1 == s2);
}

operator<=()

bool operator<=(const My_Str::string& s1, const My_Str::string& s2)
{return s1 < s2 || s1 == s2;
}

operator>()

bool operator>(const My_Str::string& s1, const My_Str::string& s2)
{return !(s1 <= s2);
}

operator>=()

bool operator>=(const My_Str::string& s1, const My_Str::string& s2)
{return !(s1 < s2);
}

全部代码

String.h

#pragma once
#include<iostream>
#include<string>
#include<assert.h>
//完成string的增删查改复写
namespace My_Str //防止污染std命名空间
{class string{private:char* _str;int _size;//有效字符的个数int _capacity;//有效字符的容量,不包含'\0',但是因为开辟空间时多开辟一个,//因此默认预留了一个'\0',方便执行C函数public:static const size_t npos;//全缺省的构造函数,不能用空指针赋值,会产生野指针问题string(const char* str="");//自定义拷贝构造,// 1. 在不用引用的情况下, 实参到形参的传递也是一种拷贝// 2.默认的拷贝构造是浅拷贝,在有需要动态管理的成员时,防止浅拷贝造成的对同一空间多次delete问题//这也是现代写法的原因string(const string& s);//析构函数~string();void swap(string& s);//赋值运算符string& operator=(const string& s);//字符首地址.const char* c_str();const char* c_str()const;size_t size();size_t size()const ;char& operator[](size_t pos);const char& operator[](size_t pos)const;//迭代器,暂时理解为指针//普通对象迭typedef char* iterator;iterator begin();iterator end();//常对象typedef const char* const_iterator;const_iterator begin()const;const_iterator end()const;//扩容void reserve(size_t n);//不支持缩小扩void resize(size_t n, char ch='\0');//支持缩小扩//插入,更多的是使用+=//头插,一般不建议使用,效率低string& insert(size_t pos, const char ch);string& insert(size_t pos, const char *str);//尾插,//单个字符,只需要按情况扩容2倍即可,// 但是对于字符串,其个数不可空,因此扩容时依情况确定。//void push_back(char ch);void append( const char* str);string& operator +=(const char ch);string& operator +=(const char* str);//删string& erase(size_t pos = 0, size_t len = npos);//查size_t find(char ch);size_t find(const char* str, size_t pos = 0);};
}std::ostream &operator<<(std::ostream& out, const My_Str::string& s);std::istream& operator >> (std::istream& in,  My_Str::string& s);bool operator<(const My_Str::string& s1, const My_Str::string& s2);
bool operator==(const My_Str::string& s1, const My_Str::string& s2);
bool operator!=(const My_Str::string& s1, const My_Str::string& s2);bool operator<=(const My_Str::string& s1, const My_Str::string& s2);bool operator>(const My_Str::string& s1, const My_Str::string& s2);bool operator>=(const My_Str::string& s1, const My_Str::string& s2);

String.cpp

#define _CRT_SECURE_NO_WARNINGS
#include"String.h"const size_t My_Str::string::npos=-1;//全缺省的构造函数,对于空字符串,_size为0,在扩容时要留意
My_Str::string::string(const char* str):_size(strlen(str)), _capacity( _size)
{_str = new char[_capacity+1];strcpy(_str, str);
}//拷贝构造,注意
// 1. 在不用引用的情况下, 实参到形参的传递也是一种拷贝
// 2.在有需要动态管理的成员时,防止浅拷贝造成的对同一空间多次delete问题
// 3.这也是现代写法的原因
My_Str::string::string(const string& s){一般写法//_str = new char[strlen(s._str) + 1];//strcpy(_str, s._str);//_size = s._size;//_capacity = s._capacity;现代写法,本质是对代码的复用和对局部变量函数栈帧后销毁的操作string tmp(s._str);//调用了自定义的构造函数_str = nullptr;//这步必须要有,否则就是对随机值进行deleteswap(tmp);//string下的swap函数//std::swap(_str,tmp._str);//标准库下的swap模板函数_size = tmp._size;_capacity = tmp._capacity;}My_Str::string::~string()//析构函数
{delete[]_str;_str = nullptr;_size =_capacity= 0;}void My_Str::string::swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}My_Str::string& My_Str::string::operator=(const My_Str::string& s)
{//自身的赋值//if (&s != this)//{// //一般写法://    char *tmp = new char[s._size + 1];//防止申请失败,C++的new会抛异常,未学//   strcpy(tmp, s._str);//  delete[]_str;//释放原先的空间//    _str = tmp;//局部变量销毁后,堆的空间仍存在。// _size = s._size;// _capacity = s._capacity;//}//现代写法,不用考虑自身赋值和多次释放同一空间的问题string tmp(s);//std::swap(_str, tmp._str);_size = tmp._size;_capacity = tmp._capacity;return *this;}//字符首地址.
const char* My_Str::string::c_str()
{return _str;
}const char* My_Str::string::c_str()const
{return _str;
}size_t My_Str::string::size()
{return _size;
}size_t My_Str::string::size()const
{return _size;
}char& My_Str::string::operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}const char& My_Str::string::operator[](size_t pos)const
{assert(pos < _size);return _str[pos];
}My_Str::string::iterator My_Str::string::begin()
{return _str;}
My_Str::string::iterator My_Str::string::end()
{return _str + _size ;}My_Str::string::const_iterator My_Str::string::begin()const
{return _str;
}
My_Str::string::const_iterator My_Str::string::end()const
{return _str + _size;}void My_Str::string::reserve(size_t n)
{if(n > _capacity)   {char* tmp = new char[n+1];strcpy(tmp,_str);delete[]_str;_str = tmp;_capacity = n;}
}void  My_Str::string::resize(size_t n, char ch)
{//resize不支持缩小_capacity;if (n <= _size){_str[n] = '\0';_size = n;}else {if (n > _capacity){reserve(n);}memset(_str + _size, ch, n - _size);_str[_size] = '\0';}
}void My_Str::string::push_back(char ch)
{if (_size == _capacity){reserve(_capacity==0?4:_capacity*2);}_str[_size] = ch;++_size;_str[_size] = '\0';
}void  My_Str::string::append(const char* str)
{size_t len = strlen(str);if ((len + _size) > _capacity){reserve(len + _size);}strcpy(_str + _size, str);
}My_Str::string& My_Str::string::operator +=(const char ch)
{push_back(ch);return *this;
}My_Str::string& My_Str::string::operator +=(const char* str)
{append(str);return *this;
}size_t My_Str::string::find(char ch)
{for (size_t i = 0; i < _size; ++i){if (ch == _str[i]){return i;}}return npos;
}
size_t My_Str::string::find(const char* str, size_t pos)
{assert(pos < _size&& strlen(str)<_size);const char* ret = strstr(_str, str);if (ret != nullptr){return ret - _str;}return npos;
}
My_Str::string& My_Str::string::insert(size_t pos, const char ch)
{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//因为是无符号整型,在减运算中要注意死循环问题size_t end = _size+1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;return *this;
}
My_Str::string& My_Str::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;}strncpy(_str + pos, str,len);//strcpy会将'\0添加进去'return *this;
}
My_Str::string& My_Str::string::erase(size_t pos , size_t len )
{assert(pos < _size);if (len == npos || len >= (_size - pos))//删除全部{_str[pos] = '\0';_size = pos;}else {strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;
}
//
//"abcd" "abcd" false
//"abcd" "abcde"true
//"abcde" "abcd"false
bool operator<(const My_Str::string& s1, const My_Str::string& s2)
{size_t i1 = 0;size_t i2 = 0;while ( i1< s1.size()&& i2<s2.size()){if (s1[i1] < s2[i2]){return false;}++i1;++i2;}return i1 < s1.size() ? false : true;}
bool operator==(const My_Str::string& s1, const My_Str::string& s2)
{size_t i1 = 0;size_t i2 = 0;while (i1 < s1.size() && i2 < s2.size()){if (s1[i1] != s2[i2]){return false;}++i1;++i2;}return i1 == s1.size() && i2 == s2.size() ? true : false;
}
bool operator!=(const My_Str::string& s1, const My_Str::string& s2)
{return !(s1 == s2);
}bool operator<=(const My_Str::string& s1, const My_Str::string& s2)
{return s1 < s2 || s1 == s2;
}
bool operator>(const My_Str::string& s1, const My_Str::string& s2)
{return !(s1 <= s2);
}bool operator>=(const My_Str::string& s1, const My_Str::string& s2)
{return !(s1 < s2);
}std::ostream& operator<<(std::ostream& out, const My_Str::string& s)
{for (int i = 0; i < s.size(); ++i)//有限次的循环,可以避免字符串中间含有'\0'的情况{  out << s[i];}//cout<<s.c_str();return out;
}std::istream& operator >> (std::istream& in,  My_Str::string& s)
{char ch = 0;//ch = getchar();//while (ch != ' '&&ch!='\n')//{//    s += ch;//    ch = getchar();//}ch = in.get();while (ch != ' '&&ch!='\n'){s += ch;ch = in.get();}return in;
}

总结

在处理边界问题,一定要仔细,否则程序可能会CREASH

STL--String类的常用功能复写相关推荐

  1. JavaSE学习总结(八)常用类(上)Object类==与equals方法的区别浅克隆的特点Scanner类String类String两种创建对象方式的区别String类的各种功能

    JavaSE学习总结(八)常用类(上)/Object类/==与equals方法的区别/浅克隆的特点/Scanner类/String类/String两种创建对象方式的区别/String类的各种功能 常用 ...

  2. C++ string类和常用接口的实现

    目录 C++ string类 string类的常用接口说明 string类(实现常用接口) C++ string类 值得注意的是 , string不是STL的容器,string是basic_strin ...

  3. C++ 笔记(22)— STL string 类(字符串赋值、访问、拼接、查找、翻转、大小写转换)

    1. 实例化和赋值 STL string #include <string> #include <iostream>int main () {using namespace s ...

  4. 第三次学JAVA再学不好就吃翔(part52)--String类的其他功能

    学习笔记,仅供参考 文章目录 String类 String类的其他功能 replace方法 trim方法 compareTo方法 compareToIgnoreCase方法 举几个例子 String类 ...

  5. 第三次学JAVA再学不好就吃翔(part51)--String类的转换功能

    学习笔记,仅供参考 文章目录 String类 String类的转换功能 getBytes方法 toCharArray方法 valueOf方法 toLowerCase方法 toUpperCase方法 c ...

  6. 第三次学JAVA再学不好就吃翔(part49)--String类的获取功能

    学习笔记,仅供参考 文章目录 String类 String类的获取功能 length方法 charAt方法 indexOf方法 lastIndexOf方法 substring方法 举几个例子 Stri ...

  7. 第三次学JAVA再学不好就吃翔(part48)--String类的判断功能

    学习笔记,仅供参考 文章目录 String类 String类的判断功能 equals方法 equalsIgnoreCase方法 contains方法 startsWith方法 endsWith方法 i ...

  8. 17.Java常用实用类之String类中常用的方法以及一般应用场景,final关键字

    文章目录 1.String类学习 1.1.什么是String类 1.2.String类中常用的方法 1.2.1. 构造方法 1.2.2.public int length() 1.2.3.public ...

  9. c++ string类_C++|细说STL string类概貌及底层细节

    C语言中的字符串称为C风格字符串,是一个以'0'结尾的字符数组,string.h库只提供了有限.不甚安全的字符串操作函数.char str[]只能定义编译期确定大小的字符串,而保存在堆内存的动态字符数 ...

最新文章

  1. 推荐7个冷门逆天的网站,每一款都是精品!
  2. ssh免密登录linux服务器
  3. 透彻理解Spring事务设计思想之手写实现
  4. ibatis提示Unable to load embedded resource from assembly Entity.Ce_SQL.xml,Entity.
  5. 麦迪时刻的视频和图片
  6. 无法隐藏php thinkphp,thinkphp 在隐藏index.php和Home模块时有问题
  7. 链表面试题1:反转单链表,不带头结点。
  8. Sublime text3装入插件Anaconda
  9. 年味十足的手绘年画风新年春节海报PSD模板
  10. python排序方法_python内置的排序方法
  11. 嗅觉计算机应用,sensonic计算机嗅觉分析仪
  12. Docker-CE 入门
  13. Oracle bpm实现oa,谈谈BPM、工作流引擎与OA的关系
  14. VS C++ 重新编译
  15. QQ自动发送消息——维持群聊炽焰
  16. PI3体验之无线网AP模式设定及热点分享
  17. 如何在Word中优雅地插入代码块,论文、竞赛专用
  18. 从live555 实战中了解MakeFile
  19. 10以内的分解与组成怎么教_“10以内数的组成”训练方法
  20. 如何判断函数凸或非凸?

热门文章

  1. 任正非谈接班人要求:要具有对新技术与客户需求的深刻理解
  2. js读取注册表然后打开指定程序
  3. java piggy,PiggyMetrics windows 部署
  4. 购物网站(内容页面)
  5. 烤仔观察 | 哈耶克最后的预言
  6. Lecture 13: Bernoulli Process
  7. 电吉他伤感独奏曲《永远之后》张俊文
  8. 俺常去(用)滴地方~
  9. English Word —— Day 11(discipline——editorial)
  10. 单位阶跃信号是周期信号吗_这些无厘头微信号,扎你心了吗?