C++入门string类常用接口函数介绍 + 进阶【模拟实现string接口函数】_字符串类接口函数

结尾

查漏补缺:Java岗 千+道面试题Java基础+全家桶+容器+反射+异常等

这不止是一份面试清单,更是一种”被期望的责任“,因为有无数个待面试者,希望从这篇文章中,找出通往期望公司的”钥匙“,所以上面每道选题都是结合我自身的经验于千万个面试题中经过艰辛的两周,一个题一个题筛选出来再次对好答案和格式做出来的,面试的答案也是再三斟酌,深怕误人子弟是小,影响他人仕途才是大过,也希望您能把这篇文章分享给更多的朋友,让他帮助更多的人,帮助他人,快乐自己,最后,感谢您的阅读。

由于细节内容实在太多啦,在这里我花了两周的时间把这些答案整理成一份文档了,在这里只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

需要注意的一些string成员函数c_str

string s("hello word");
    cout << s << endl;	
    //自定义类型的s在输出的时候会
    //调用重载的 operator<< (ostream& os, const string& str);
    cout << s.c\_str() << endl;
    //s.c\_str()函数返回的是一个c风格的字符串他是内置类型,
    //使用的是全局的operator<<(cout, const char \*) 
    
    cout << "------------------" << endl;
    //所以会在输出的时候各不相同
    s.resize(20);
    s += "!!!";
    cout << s << endl;
    cout << s.c\_str() << endl;

效果:
1、调用内置类型的operator<<(cout, const char *) 输出字符串的时候遇到‘\0’就会停止
2、调用自定义类型的operator<< (ostream& os, const string& str)即使遇到‘\0’也不会停止,只有把字符串给完全遍历完了才会停止遍历
在这里插入图片描述

删除操作:

void functest3() 
{
    string s("abcdefg");
    //函数原型:string& erase (size\_t pos = 0, size\_t len = npos);
    //函数功能:指定pos位置处开始,删除len个长度的字符
    s.erase(1,2);
    cout << s << endl;//adefg
}

查找操作:
从前往后

//string拷贝构造函数原型: 这里会给缺省值
//string (const string& str, size\_t pos, size\_t len = npos);

string buff1("test.cpp");
int pos = buff1.find('.');
if (pos != string::npos) 
//npos值是-1,类型是无符号整形,所以是整形的最大值,字符串并不会存储这么长
{
    string buff2(buff1,pos, buff1.size() - pos);
    //调用拷贝构造函数创建buff2对象,
    //从pos位置开始截取len个长度的字符串创建一个string对象
    
    //也可以使用这种写法去截取后缀:
    //即使使用了函数提供的缺省值也并不用过于担心,npos是最大值也好
   	//只会截取有效内容,超过不管
    string buff3(buff1,pos);

	//不调用拷贝构造函数同样也能做到, substr截取
	//函数原型:string substr (size\_t pos = 0, size\_t len = npos) const;
	string buff4 = buff1.substr(pos);
    cout << buff2 << endl;//.cpp
}

从后往前

//函数原型:
//size\_t rfind (const string& str, size\_t pos = npos) const;
//函数功能是返回指定参数在字符串中最后一次出现的位置
string buff1("test.cpp.zip");
int pos = buff1.rfind('.');

if (pos != string::npos) 
{
    string buff2(buff1, pos);
    cout << buff2 << endl;
}

使用string成员函数查找网络域名跟协议使用举例,详细解释请看注释

//返回协议名
string GetAgreeMent(const string& s)
{
    //查找"://",找到返回该字符串的起始下标位置
    size\_t pos = s.find("://");
    if (pos != string::npos)
    {
        //由于是左闭右开区间【0,5),所以只需要pos - 0就能计算出协议名的长度
        return s.substr(0, pos - 0);
    }
    else
    {
        //找不到返回空串
        return string();
    }
}
//返回域名
string GetDomain(const string& s) 
{
    //查找"://",找到返回该字符串的起始下标位置
    size\_t pos = s.find("://");
    if (pos != string::npos) 
    {
        //计算出域名的起始下标位置,从这个位置开始查找"/"
        size\_t start = pos + 3;
        size\_t end = s.find("/", start);
        if (end != string::npos) 
        {
            //同样的左闭右开区间,开区间的位置减去闭区间的位置就是字符串的长度
            return s.substr(start, end - start);
        }
        else
        {
            //找不到返回空串
            return string();
        }
    }
    else
    {
        //找不到返回空串
        return string();
    }
    

}

void functest4() 
{
    string url1 = "https://blog.csdn.net/m0\_53421868?spm=1000.2115.3001.5343";
    string url2 = "https://bbs.csdn.net/forums/mzt";
    cout << GetDomain(url1) << endl;
    cout << GetAgreeMent(url1) << endl;

    cout << GetDomain(url2) << endl;
    cout << GetAgreeMent(url2) << endl;
}

在这里插入图片描述

string类对象的容量操作

函数名称功能说明
size(重点)统计字符个数
length返回字符串有效字符长度
capacity返回空间总大小
empty (重点)检测字符串释放为空串,是返回true,否则返回false
clear (重点)清空有效字符,将size置零
reserve (重点)为字符串预留空间
resize (重点)将有效字符的个数该成n个,多出的空间用字符c填充

测试:

string s;
s.resize(10);//插入10个'\0',默认以'\0'填充
cout << s << endl;

string s1;
s1.resize(10,'x');//指定插入10个字符,以'x'填充
cout << s1 << endl;

string s3("hello word");
s3.resize(20,'x'); //将空间扩容到20,多出来的空间用'x'填充,并且是尾插的方式
cout << s3 << endl;

需要注意的一些增容函数

与resize函数相似的有reserve 他并不会改变空间的内容,只做增容
在这里插入图片描述

深浅拷贝问题

浅拷贝一般都是在拷贝构造一个对象的时候完成值拷贝的一个过程,因为我们不写编译器默认的生成的拷贝构造函数完成的是浅拷贝,这样对象出了作用域开始调用析构函数的时候会导致同一块空间被释放两次,就会引发程序崩溃

namespace mzt 
{
    class string 
    {
    public:
   		 //重载operator<<
        friend  ostream &operator<< (ostream &out, string &str)
        {
            out << str._str << endl;
            return out;
        }
        
        string(const char\* str = "")
            : \_str(new char[strlen(str) + 1])
            , \_size(0)
            ,\_capacity(0)
        {
            strcpy(_str, str);
        }
        //我们不写编译器会默认生成一个拷贝构造函数完成浅拷贝
       /\* string(const string & str) 
 : \_str(new char[strlen(str.\_str) + 1])
 {
 strcpy(\_str, str.\_str);
 }\*/
        
        ~string()
        {
            delete[] _str;
            _str = nullptr;
        }
    private:
        char\* _str;
        size\_t _capacity;
        size\_t _size;
    };
    void stringtest()
    {
        mzt::string s("hello world");
        mzt::string s1(s);
        cout << s;
        cout << s1;
    }
}

即使打印出了hello world,但是这个程序还是存在问题,因为两个对象的_str指针指向的是同一块空间所以当被delete的时候就会被析构两次,这是不被允许的,解决办法深拷贝,重新创建一个空间,并把值存过去,让两个对象之间的内容互不影响

在这里插入图片描述

深拷贝传统写法

namespace mzt 
{
    class string 
    {
    public:
        friend  ostream &operator<< (ostream &out, string &str)
        {
            out << str._str << endl;
            return out;
        }
        string(const char\* str = "")
            : \_str(new char[strlen(str) + 1])
            , \_size(0)
            ,\_capacity(0)
        {
            strcpy(_str, str);
        }
        //我们不写编译器会默认生成一个拷贝构造函数完成浅拷贝
        //解决办法深拷贝,开辟一块新的空间,把值拷贝过去
        string(const string & str) 
            : \_str(new char[strlen(str._str) + 1])
        {
           strcpy(_str, str._str);
        }
        //重载operator=也是一样的做法,
        //开空间拷贝值避免出现浅拷贝的问题
        string& operator=(const string& s) 
        {
            if (this != &s) 
            {
                delete[] _str;
                _str = new char[strlen(s._str) + 1];
                strcpy(_str, s._str);
            }
            return \*this;
        }
        
        ~string()
        {
            delete[] _str;
            _str = nullptr;
        }
        
    private:
        char\* _str;
        size\_t _capacity;
        size\_t _size;
    };
    
    void stringtest()
    {
        mzt::string s("hello world");
        mzt::string s1(s);
        cout << s;
        cout << s1;
    }
}

在这里插入图片描述

深拷贝的现代写法

推荐使用现代深拷贝的方法:
原因1: 代码简洁
原因2:可读性强

//我们不写编译器会默认生成一个拷贝构造函数完成浅拷贝
//解决办法做深拷贝
string(const string &s)
	: \_str(nullptr)
	//\_str必须初始化为nullptr,才去交换tmp指针,
	//否则\_str就是野指针了,当tmp出了作用域析构野指针会有非法内存访问
{
    //调用构造函数利用s.\_str做参数构造临时对象
    string tmp(s._str);
    //将临时对象的指针\_str和this.\_str一交换
    swap(tmp._str, _str);
    //临时对象出了作用域就销毁了,会自动调用它的析构函数
}

//拷贝赋值运算符现代写法
string& operator=(string s)
//s通过调用拷贝构造函数完成的深拷贝
{
    //还是一样的思路,由于拷贝构造函数已经被我们实现了,
    //所以就不会存在浅拷贝的问题,所以通过值传递,即使
    //栈帧被销毁了,两个对象也互不影响,这也是一种复用的方法
    //直接上手交换指针this.\_str和s.\_str
    swap(_str, s._str);
    return \*this;
}

注意:想要复用拷贝构造函数,这里的_str必须初始化为nullptr,否则程序是会有隐患的

string(const string &s)
 :\_str(nullptr)
 //\_str必须初始化为nullptr,才去交换tmp对象的\_str指针,
 //否则\_str就是野指针了,
 //当tmp出了作用域析构野指针会有非法内存访问
{
   //利用s.\_str构造临时对象
   string tmp(s._str);
   swap(tmp._str, _str);
}

string模拟实现

构造函数

 //构造函数
string(const char\* str)
  :\_str(new char[strlen(str) + 1])
  ,\_size(strlen(str))
  ,\_capacity(_size)
  //\_capacity建议给strlen(str),
  //即使给strlen(str) + 1也可以
{
	//初始化新对象的\_str
  	strcpy(_str, str);
}

拷贝构造函数

 //拷贝构造
string(const string& s)
    :\_str(nullptr)
    //指针初始化给nullptr,为了交换给tmp对象,
    //即使tmp对象出了作用域析构的时候也不受影响
    ,\_size(0)
    ,\_capacity(0)
{
    //构造一个tmp对象,交换tmp对象和对象的引用的成员属性
    string tmp(s._str);
    //在string中自定义的交换函数,详细看下面
    Swap(tmp);
}

交换函数

 //交换
void Swap(string& tmp)
{
    //调用全局的模板交换函数,交换对象的成员变量
    ::swap(tmp._size,_size);
    ::swap(tmp._capacity, _capacity);
    ::swap(tmp._str, _str);
}

在string定义一个局部的交换函数,调用C++模板库的全局函数
在这里插入图片描述
从官网的文档可以看出swap是一个模板函数,如果直接交换两个对象会涉及到三个深拷贝的问题,所以使用交换对象的成员这种方式会更好,同样的拷贝赋值运算符也是一样的要交换成员变量就调用swap函数就行

拷贝赋值运算符

 //拷贝赋值运算符
string& operator=(string s) 
//调用拷贝构造函数创建的s对象,这里是值传递并不会影响到外面对象
 {
 	//交换s和this对象的成员变量
     Swap(s);
     return \*this;
 }

获取对象的成员属性

加上const,即使来调用这个成员函数的this的是属于const权限也可以,非const权限也可以

//返回对象的大小
int size() const
{
    return  _size;
}
//返回C风格的字符串
char\* C\_str() const
{
    return _str;
}

重载可读可写[ ]和可读[ ]

//可读operator[]
const char& operator[](size\_t i) const
 {
     assert(i < _size);
     return _str[i];
     //返回下标为i位置的字符,const修饰的是this,
     //所以this对象是不能修改的,并且返回引用的时候也要加const
 }
 //可读可写operator[],简单粗暴
 char& operator[](size\_t i)
 {
     assert(i < _size);
     return _str[i];
 }

定义迭代器

typedef char\* iterator;
//定义begin迭代器
iterator begin() 
{
   return _str;
}
//定义end迭代器
iterator end()
{
   return _str + _size;
}

范围for会被编译器替换成迭代器形式,范围for的底层是有迭代器支持的,这里为了让读者更醒目的看到现象,那么就用范围for遍历一次对象证明

开始遍历:

for (auto ch : s) 
{
   cout<< ch <<" ";
}

效果:
在这里插入图片描述
迭代器没去掉前可以跑出结果,如果当迭代器去掉后,程序立马跑出一连串的错误信息
在这里插入图片描述

增容处理

void push\_back(char ch) 
{
    //空间满了调用reverse扩容
    if (_size == _capacity) 
    {
    	//原来空间上扩容二倍
        reverse(_capacity \* 2);
    }
    //扩容后在字符串后面加一个‘\0’
    _str[_size] = ch;
    _str[_size + 1] = '\0';
    _size++;
}
//追加一个字符串
void append(const char \* str) 
{
	//统计需要增容多少的空间
    size\_t len = _size + strlen(str);
    if (len > _capacity) 
    {
    	//扩容len个长度
        reverse(len);
    }
    //将字符串str追加在\_str + \_size的位置处
    strcpy(_str + _size, str);
    //更新\_size的位置
    _size = len;
}

rsize和reverse函数

1、reserve (重点) 为字符串预留空间,空间不够就扩容,够就不需要处理
2、resize (重点) 将有效字符的个数该成n个,多出的空间用字符c填充

//开空间并初始化,size也要动
 void resize(size\_t n, char ch = '\0')
{
	//如果n要小于size,那么就将\_str的大小控制在n,有效字符也控制在n
   if (n < _size) 
   {
       int end = _size - n;
       _str[n] = '\0';
       _size -= end;
   }
   else 
   {
   		//n大于实际空间的大小,从\_size位置开始初始化后面的空间
       if (n > _capacity) 
       {
           reserve(n);
       }
       for (size\_t i = _size; i < n; i++) 
       {
           _str[i] = ch;
       }
       _size += n;
       _str[_size] = '\0'
   }
}

//扩容不修值
void reverse(size\_t size) 
{
    //空间不够,扩容至size大小
    if (size > _capacity) 
    {
        //多开一个空间是为了保存‘\0’
        char\* tmp = new char[size + 1];
        //释放旧的空间
        delete[]_str;
        //让\_str指向新的空间,并把值给拷贝过去
        strcpy(tmp,_str);
        _str = tmp;
    }
    else 
    {
        //空间够不做扩容处理
        return;
    }
}

指定位置插入操作

//在pos之前插入数据
string& insert(size\_t pos,char ch) 
{
    assert(pos <= _size);
    //空间满了就扩容
    if (_size == _capacity) 
    {
        reverse(_capacity == 0 ? 4 : _capacity \* 2);
    }
    //整体字符从‘\0’位置开始往后挪动1个位置
    size\_t end = _size + 1;
    while (end > pos) 
    {
        _str[end] = _str[end - 1];
        end--;
    }
    //在第一个位置放入ch
    _str[end] = ch;
    _size++;
   
    return \*this;
}

//pos之前插入字符串
string& insert(size\_t pos, const char\* str)
{
    size\_t len = strlen(str);
    if (len + _size > _capacity) 
    {
        reverse(len + _size);
    }
    //【end,pos】区间的字符整体往后挪动len个长度,
    char\* end = _str + _size;
    while (end >= _str + pos) 
    {
        \*(end + len) = \*end;
        end--;
    }
    //从pos位置开始将str的字符串覆盖在这个位置处,一直到走到len的位置
    strncpy(_str + pos, str, len);
    _size += len;
    return \*this;
}

删除

//指定删除pos位置的len个长度的空间
string& Erese(size\_t pos, size\_t n = npos)
{
	assert(pos <= _size);
	size\_t len = _size - pos;
	//剩余的长度小于要删除的长度,则后面的字符全部删除
	if (n >= len)
	{
	   _str[pos] = '\0';
	   _size = pos;
	}
//剩余长度大于删除的长度,只将后面的字符往前覆盖掉前面len个长度的字符
	else	
	{
	    strcpy(_str + pos, _str + pos + n);
	    _size -= n;
	}
return \*this;
}

查找

//查找字符
size\_t find(char ch, size\_t pos = 0)
{
    assert(pos < _size);
    for (size\_t i = pos; i < _size; i++)
    {
        if (_str[i] == ch)
        {
            return i;
        }
    }
    return npos;
}
//查找字符串,返回相同字符串的起始地址
size\_t find(const char\* str, size\_t pos = 0)
{
    assert(pos < _size);
    const char\* ret = strstr(_str + pos, str);
    if (ret)
        return ret - _str;
    else
        return npos;
}

重载输入输出

 void clear() 
 {
     _str[0] = '\0';
     _size = 0;
 }
 
 friend istream& operator>>(istream& in,string &s)
 {
     s.clear();
     char ch = in.get();
     //cin遇到 ' '和‘\n’不再读取
     while (ch != ' ' && ch != '\n')
     {
         s += ch;
         ch = in.get();
     }
     //返回对象可以连续输入
     return in;
 }
 friend ostream& operator<<(ostream& out, string& s)
 {


# 《MySql面试专题》

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/bbd84fa8f8ac9369707e3b4ebbcd44a0.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/109268d8927d03c87129539db45fa250.webp?x-oss-process=image/format,png)

# 《MySql性能优化的21个最佳实践》

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/5ccca5c2a627c41955ffddd25cea8520.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/1748faf50fe5146447ad97e02cd1a6d7.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/e79a32d5653908cc508e3c31bdce7ce4.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/a2c10942213ba0fb7a800b35c375924a.webp?x-oss-process=image/format,png)

# 《MySQL高级知识笔记》

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/6f934743abc0c1efdbdc1e8267a0b76e.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/cd2e6ddbce13e7cd760d0479389a876c.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/1bce9192a7077037eee8ddf952c6ec05.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/1b296df485da0cbfc4d5838f9b29575e.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/325b4bff2e7d6d53c9768647176269e3.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/1aabf70e06a56cfbcb0f01db298c6c95.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/d817d2ac68843605c12724ffbf76ae09.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/cc7e73c7d8f9b858d731d0cd883d6baa.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/f090e6558e328ae4daf058ee28d3a616.webp?x-oss-process=image/format,png)

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/82c59b80f28855b502d2d33aeb6845b1.webp?x-oss-process=image/format,png)

文中展示的资料包括:**《MySql思维导图》《MySql核心笔记》《MySql调优笔记》《MySql面试专题》《MySql性能优化的21个最佳实践》《MySq高级知识笔记》**如下图

![全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好](https://img-blog.csdnimg.cn/img_convert/c9961fb758cac0601d36ee1116214b15.webp?x-oss-process=image/format,png)

**关注我,点赞本文给更多有需要的人**

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618154847)**

mg-TbZTGV3C-1715816606827)]

[外链图片转存中...(img-QMaAkMkA-1715816606827)]

# 《MySQL高级知识笔记》

[外链图片转存中...(img-4IXzYCn4-1715816606827)]

[外链图片转存中...(img-jFtZDLMI-1715816606828)]

[外链图片转存中...(img-xrkRVtaW-1715816606828)]

[外链图片转存中...(img-NcWuuTEK-1715816606828)]

[外链图片转存中...(img-RbyDwMsT-1715816606828)]

[外链图片转存中...(img-yiYjoVY9-1715816606829)]

[外链图片转存中...(img-dX9CBck3-1715816606829)]

[外链图片转存中...(img-d6V3eOHB-1715816606829)]

[外链图片转存中...(img-Q6es4ca8-1715816606830)]

[外链图片转存中...(img-3W0laOUG-1715816606830)]

文中展示的资料包括:**《MySql思维导图》《MySql核心笔记》《MySql调优笔记》《MySql面试专题》《MySql性能优化的21个最佳实践》《MySq高级知识笔记》**如下图

[外链图片转存中...(img-3lJlpwds-1715816606830)]

**关注我,点赞本文给更多有需要的人**

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/topics/618154847)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618154847)**

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值