STL--String类的常用功能复写

简介:

@[TOC]

前言:

  • 本文介绍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;//这步必须要有,否则就是对随机值进行delete
    swap(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;//这步必须要有,否则就是对随机值进行delete
    swap(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

相关文章
|
26天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
42 2
|
2月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
68 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
2月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
52 2
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
26 1
|
2月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
64 4
|
2月前
|
存储 安全 Java
【一步一步了解Java系列】:认识String类
【一步一步了解Java系列】:认识String类
28 2
|
2月前
|
存储 编译器 程序员
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
76 2
|
2月前
|
C语言 C++
C++番外篇——string类的实现
C++番外篇——string类的实现
22 0
|
2月前
|
C++ 容器
C++入门7——string类的使用-2
C++入门7——string类的使用-2
23 0
|
2月前
|
C语言 C++ 容器
C++入门7——string类的使用-1
C++入门7——string类的使用-1
23 0