【C++】STL——string

前言

本篇博客我们来看一下C++标准库里STL库中将字符串相关属性自定义成string类,并且标准库里给出很多关于string的库函数,我们一起来了解一下string

💓 个人主页:小张同学zkf

⏩ 文章专栏:C++

若有问题 评论区见📝

🎉欢迎大家点赞👍收藏⭐文章 ​

 

 

目录

​编辑 

1. string类介绍

1.1string

1.2auto和范围for

2.string类的常用接口

2.1string的常见构造

2.2string类对象的容量操作

2.3string类对象的访问及遍历操作

2.4string类对象的修改操作

2.5string类非成员函数

3.string类的模拟实现


 

1. string类介绍

1.1string

链接:string类介绍

使用 string 类时,必须包含 #include 头文件以及 using namespace std ;

 


1.2auto和范围for

auto 关键字
在早期 C/C++ auto 的含义是:使用 auto 修饰的变量,是具有自动存储器的局部变量,后来这个
不重要了。 C++11 中,标准委员会变废为宝赋予了 auto 全新的含义即: auto 不再是一个存储类型
指示符,而是作为一个新的类型指示符来指示编译器, auto 声明的变量必须由编译器在编译时期
推导而得
auto 声明指针类型时,用 auto auto* 没有任何区别,但用 auto 声明引用类型时则必须加 &
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际
只对第一个类型进行推导,然后用推导出来的类型定义其他变量 auto 不能作为函数的参数,可以做返回值,但是建议谨慎使用
auto 不能直接用来声明数组

举个例子

#include<iostream>
using namespace std ;
int func1 ()
{
return 10 ;
}
// 不能做参数
void func2 ( auto a )
{}
// 可以做返回值,但是建议谨慎使用
auto func3 ()
{
return 3 ;
}
int main ()
{
int a = 10 ;
auto b = a ;
auto c = 'a' ;
auto d = func1 ();
// 编译报错 :rror C3531: “e”: 类型包含 “auto” 的符号必须具有初始值设定项
auto e ;
cout << typeid ( b ). name () << endl ;
cout << typeid ( c ). name () << endl ;
cout << typeid ( d ). name () << endl ;
int x = 10 ;
auto y = & x ;
auto * z = & x ;
auto & m = x ;
cout << typeid ( x ). name () << endl ;
cout << typeid ( y ). name () << endl ;
cout << typeid ( z ). name () << endl ;
auto aa = 1 , bb = 2 ;
// 编译报错: error C3538: 在声明符列表中, “auto” 必须始终推导为同一类型
auto cc = 3 , dd = 4.0 ;
// 编译报错: error C3318: “auto []”: 数组不能具有其中包含 “auto” 的元素类型
auto array [] = { 4 , 5 , 6 };
return 0 ;
}

范围for 

对于一个 有范围的集合 而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的 for 循环。 for 循环后的括号由冒号 分为两部分:第一部分是范围 内用于迭代的变量,第二部分则表示被迭代的范围 ,自动迭代,自动取数据,自动判断结束。 范围for 可以作用到数组和容器对象上进行遍历范围for 的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。

举个例子

#include<iostream>
#include <string>
#include <map>
using namespace std ;
int main ()
{
int array [] = { 1 , 2 , 3 , 4 , 5 };
// C++98 的遍历
for ( int i = 0 ; i < sizeof ( array ) / sizeof ( array [ 0 ]); ++ i )
{
array [ i ] *= 2 ;
}
for ( int i = 0 ; i < sizeof ( array ) / sizeof ( array [ 0 ]); ++ i )
{
cout << array [ i ] << endl ;
}
// C++11 的遍历
for ( auto & e : array )
e *= 2 ;
for ( auto e : array )
cout << e << " " << endl ;
string str ( "hello world" );
for ( auto ch : str )
{
cout << ch << " " ;
}
cout << endl ;
return 0 ;
}

 


2.string类的常用接口

2.1string的常见构造

void Teststring ()
{
string s1 ; // 构造空的 string 类对象 s1
string s2 ( "hello world" ); // C 格式字符串构造 string 类对象 s2
string s3 ( s2 ); // 拷贝构造 s3
}

2.2string类对象的容量操作

 

注意:
1. size() length() 方法底层实现原理完全相同,引入 size() 的原因是为了与其他容器的接
口保持一致,一般情况下基本都是用 size()
2. clear() 只是将 string 中有效字符清空,不改变底层空间大小。
3. resize(size_t n) resize(size_t n, char c) 都是将字符串中有效字符个数改变到 n 个,不
同的是当字符个数增多时: resize(n) 0 来填充多出的元素空间, resize(size_t n, char
c) 用字符 c 来填充多出的元素空间。注意: resize 在改变元素个数时,如果是将元素个数
增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
4. reserve(size_t res_arg=0) :为 string 预留空间,不改变有效元素个数,当 reserve 的参
数小于 string 的底层空间总大小时, reserver 不会改变容量大小。

2.3string类对象的访问及遍历操作


2.4string类对象的修改操作

 

注意:
1. string 尾部追加字符时, s.push_back(c) / s.append(1, c) / s += 'c' 三种的实现方式差
不多,一般情况下 string 类的 += 操作用的比较多, += 操作不仅可以连接单个字符,还可
以连接字符串。
2. string 操作时,如果能够大概预估到放多少字符,可以先通过 reserve 把空间预留
好。

 


2.5string类非成员函数

 

string 类中还有一些其他的操作,这里不一一列举,大家在需要用到时不明白了查文档即可。

 


3.string类的模拟实现

上面已经对 string 类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让
学生自己来模拟实现 string 类,最主要是实现 string 类的构造、拷贝构造、赋值运算符重载以及析
构函数。大家看下以下 string 类的实现是否有问题?
// 为了和标准库区分,此处使用 String
class String
{
public :
/*String()
:_str(new char[1])
{*_str = '\0';}
*/
//String(const char* str = "\0") 错误示范
//String(const char* str = nullptr) 错误示范
String ( const char* str = "" )
{
// 构造 String 类对象时,如果传递 nullptr 指针,可以认为程序非
if ( nullptr == str )
{
assert ( false );
return ;
}
_str = new char [ strlen ( str ) + 1 ];
strcpy ( _str , str );
}
~String ()
{
if ( _str )
{
delete [] _str ;
_str = nullptr ;
}
}
private :
char* _str ;
};
// 测试
void TestString ()
{
String s1 ( "hello bit!!!" );
String s2 ( s1 );
}

 

说明:上述 String 类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认 的,当用 s1 构造 s2 时,编译器会调用默认的拷贝构造。最终导致的问题是, s1 s2 共用同一块内 存空间,在释放时同一块空间被释放多次而引起程序崩溃 ,这种拷贝方式,称为 浅拷贝
浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来 。如果 对象中管理资源 ,最后就会 导致 多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该 资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规 。 就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我夺,玩具损坏。
可以采用深拷贝解决浅拷贝问题,即: 每个对象都有一份独立的资源,不要和其他对象共享 。父母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。
深拷贝
如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

 

传统写法的string类

class String
{
public :
String ( const char* str = "" )
{
if ( nullptr == str )
{
assert ( false );
return ;
}
_str = new char [ strlen ( str ) + 1 ];
strcpy ( _str , str );
}
String ( const String & s )
: _str ( new char [ strlen ( s . _str ) + 1 ])
{
strcpy ( _str , s . _str );
}
String & operator = ( const String & s )
{
if ( this != & s )
{
char* pStr = new char [ strlen ( s . _str ) + 1 ];
strcpy ( pStr , s . _str );
delete [] _str ;
_str = pStr ;
}
return * this ;
}
~String ()
{
if ( _str )
{
delete [] _str ;
_str = nullptr ;
}
}
private :
char* _str ;
}

 

现代写法的string类

class String
{
public :
String ( const char* str = "" )
{
if ( nullptr == str )
{
assert ( false );
return ;
}
_str = new char [ strlen ( str ) + 1 ];
strcpy ( _str , str );
}
String ( const String & s )
: _str ( nullptr )
{
String strTmp ( s . _str );
swap ( _str , strTmp . _str );
}
// 对比下和上面的赋值那个实现比较好?
String & operator = ( String s )
{
swap ( _str , s . _str );
return * this ;
}
/*
String& operator=(const String& s)
{
if(this != &s)
{
String strTmp(s);
swap(_str, strTmp._str);
}
return *this;
}
*/
~String ()
{
if ( _str )
{
delete [] _str ;
_str = nullptr ;
}
}
private :
char* _str ;
};

结束语 

STL——string的相关知识总结完了,下一篇我们总结STL里的vector

OK,感谢观看!!!

 

  • 55
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 47
    评论
评论 47
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值