IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C++】string笔记 -> 正文阅读

[C++知识库]【C++】string笔记

string

模板:

泛型编程

函数模板

模板关键字:tmplate<calss T> //T可以随意改
template<class T> //template<typename T> 模板参数列表 - 参数类型
void Swap(T& x1, T& x2) //函数参数列表 - 参数对象
{
	T x = x1;
	x1 = x2;
	x2 = x;
}
传入不同的类型 会调用不同的实例化函数
多个类型:
    template<class T1,class T2>

模板实例化

调用Swap时 会实例化出不同类型的函数
实例化:是指在面向对象的编程中,把用类创建对象的过程称为实例化。 是将一个抽象的概念类,具体到该类实物的过程。 实例化过程中一般由类名对象名= new 类名(参数1,参数2…参数n)构成。

函数模板的实例化

template<class T>
T Add(const T& left, const T& right)//做返回值 和
{
    return left + right;
}
int main()
{
    int a1 = 10, a2 = 20;
    double d1 = 10.1, d2 = 20.2;
    cout << Add(a1, a2) << endl;
    cout << Add(d1, d2) << endl;
    
    cout << Add((double)a1, d2) << endl;
    
    //显示实例化
    cout << Add<int>(d1, d2) << endl; //指定类型
    cout << Add<double>(d1, d2) << endl; 
    
    return 0;
}
结果:
30
30.3
30.2
30
30.3

如果函数模板 和 普通函数 同时存在,优先使用普通函数

类模板

一个栈类型 只能是 int 或 double 或…
定义对象只能满足 int栈 或者double栈
想要存储多个类型 则需要建多个不同类型的 类

template<class T>
class Stack
{
privvate:
    T* _a;
    int _top;
    int _capacity;
}
int main()
{
    Stack<int> st1;//存储int
    Stack<double> st2;//存储double
}

STL:

S标准T模板L库
Vue - 前端贡献:尤雨溪

书籍推荐

《STL源码剖析》《effcrive C++》 继承多态学完看 《高质量C++》现在看

STL 六大组件

算法、容器、迭代器、配接器、仿函数、空间配置器(内存池)

string 成员函数

c++文档:

https://cplusplus.com/

编码补充

编码 - 值 – 符号建立映射
ASCII码表 - 表示英文编码表
unicode - 表示全世界文字编码表 utf-8
gbk - 中文编码表

常用的string

  • 赋值
default (1)string();
copy (2)string (const string& str);
substring (3)string (const string& str, size_t pos, size_t len = npos);
from c-string (4)string (const char* s);
  • size:计算长度

    Return length of string (public member function )

  • capacity:计算空间大小 不算\0 所以是15

    Return size of allocated storage (public member function )

  • clear:清除string内容

    Clear string (public member function )

  • reserve

    Request a change in capacity (public member function )

  • operator[ ]:出错断言assert

    Get character of string (public member function )

          char& operator[] (size_t pos);
    const char& operator[] (size_t pos) const;
    这里的引用返回时为了 修改返回对象
    
    读取
    s1[i] = s1.operator[]  的重载
    

    image-20220619201146602

    修改
    s1[i]+=1;
    

    image-20220619201539923

  • at:获取字符串的字符,出错抛异常

    Get character in string (public member function)

  • operator+=:尾插字符或字符串

    Append to string (public member function)

    string s1;
    s1 += ':';
    s1 += "hello world";
    cout << s1 << endl;
    
  • append:尾插字符串 不常用

    Append to string (public member function)

  • push_back:尾插字符 不常用

    Append character to string (public member function)

string s1;
s1.push_back('a');
s1.append("bcde");
cout << s1 << endl;

string 迭代器

	//遍历+修改
	//方式1:下标+[]  
	//返回对应位置的引用  可以直接修改s1[i]
	for (size_t i = 0; i < s1.size(); ++i)
	{
		s1[i] += 1;
	}
	for (size_t i = 0; i < s1.size(); ++i)
	{
		cout << s1[i] << " ";
	}
	cout << endl;

迭代器想象成:像指针一样的类型

end()/begin()

typedef char*iterator;
typedef const char*const_iterator;

		iterator begin();
const_iterator begin() const;

      iterator end();
const_iterator end() const;
string s1("hello world");
//方式2:迭代器(iterator)
//begin()指的是第一个元素的位置  end()是最后一个元素的【下一个】
//it像指针一样指向第一个元素 ,可以解引用,可以++
string::iterator it = s1.begin();
while (it != s1.end())
{
    *it -= 1;
    ++it;
}
it = s1.begin();//重置it的位置
while (it != s1.end())
{
    cout << *it << " ";
    ++it;
}
cout << endl;

iterator begin();

范围for会替换成迭代器

//方式3:范围for   语法糖用起来很爽很甜
// C++11   linux:-std=c++11
//把s1中的值取出来 赋值给e 自动++
//for (char& e : s1) 
for (auto& e : s1) //引用  
{
    e += 1;
}
for (auto e:s1)
{
    cout << e << " ";
}
cout << endl;

const_iterator begin() const;

const 版本只能读取 不能修改

void func(const string& s1)
{
	string::const_iterator it = s1.begin();
	//auto it = s1.cbegin(); //cbegin()和cend()代表const
	while (it != s1.end())
	{
		//*it -= 1; //不能修改
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

rbegin() / rend(): 反向迭代器

      reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
  • rbegin() 最后一个字符
  • rebing()的 ++ 是向前走
  • rend()是第一个字符的前一个
void test_string2()
{
	string s1("hello world");
	//反向迭代器
	//rbegin() 指向最后一个字符 
	//
	//反向++ 是逆向的
	//string::reverse_iterator rit = s1.rbegin(); // string::reverse_iterator是类型
	auto rit = s1.rbegin();//代替上面那句自动推到类型
	while (rit != s1.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;

	string cstr("hello world");
	func(cstr);
}

c++11 新增const迭代器

迭代器的意义是什么?

所有的容器都可以使用迭代器这种方式去访问修改

答:

对于string,下标和[]就足够好用,确实可以不用迭代器。

其他容器(数据结构)呢?

(list、map/set 并不支持下标,只有数组才支持[],这些是链表和二叉树并不支持下标+[])

所以迭代器才是通用的方式

string 增容

测试代码

void TestPushBack()
{
	string s;
	//s.reserve(1000);//申请至少能存储10000个字符的空间  不一定是1000 要空间对其

	size_t sz = s.capacity();
	cout << "capacity changed: " << sz << '\n';
	cout << "making s grow:\n";
	for (int i = 0; i < 2000; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

结果

capacity changed: 15	//本质是16 但是没算\0  有效字符位置有15个
making s grow:
capacity changed: 31	//本质是32 但是没算\0  有效字符位置有31个
capacity changed: 47
capacity changed: 70
capacity changed: 105
capacity changed: 157
capacity changed: 235

reserve:扩容,只开空间

resize:扩容+初始化

开空间,并给初始值 进行初始化

如果resize扩容比已有数据少,则会删除多余数据,不会改变空间大小

reverse也不会缩容

void test_string3()
{
	string s1;
	s1.reserve(100);
	//开空间并初始化
	string s2;
	//s2.resize(100); // 初始化的 \0
	s2.resize(100,'x');//指定字符初始化
	//resize不会把已有数据覆盖初始化,如果比已有数据小,则会除多余的数据
}

string 查找

c_str返回C格式字符串

const char* c_str() const;
void test_string4()
{
	string s("hello world");
	cout << s << endl;//流插入  size是多少 打印多少
	cout << s.c_str() << endl;//const char*  遇到\0结束
    
    //应用场景 这里fopen的第一个参数需要const char*类型的字符串
    string file("test.txt");
	FILE* fout = fopen(file.c_str(), "w");
}

find:找位置

从字符串pos位置开始往后找字符c,返回该字符在字符串中的下标

string (1)size_t find (const string& str, size_t pos = 0) const;
c-string (2)size_t find (const char* s, size_t pos = 0) const;
buffer (3)size_t find (const char* s, size_t pos, size_t n) const;
character (4)size_t find (char c, size_t pos = 0) const;

image-20220626094342788

npos:-1

size_t类型的 -1 一个很大的数

substr:查找字符串

在str中从pos位置开始,截取n个字符,然后将其返回

string substr (size_t pos = 0, size_t len = npos) const;
string file("test.txt");
FILE* fout = fopen(file.c_str(), "w");
size_t pos = file.find('.');
if (pos != string::npos)//npos是 -1  size_t全1
{
    //string suffix = file.substr(pos, file.size() - pos);
    string suffix = file.substr(pos);//直接用默认缺省值 取到最后
    cout << suffix << endl;
}

如果是连续后缀 ,要从右往左找

rfind反向找

	string file("test.txt.zip");
	FILE* fout = fopen(file.c_str(), "w");
	size_t pos = file.rfind('.');//这里用rfind
	if (pos != string::npos)
	{
		string suffix = file.substr(pos);
		cout << suffix << endl;
	}
}
  • 解析URL

    	string url("https://www.cplusplus.com/reference/string/string/rfind/");
    	//取协议
    	size_t pos1 = url.find(':');
    	string protocol = url.substr(0, pos1 - 0);
    	cout << protocol << endl;
    	//取域名
    	size_t pos2 = url.find('/',pos1+3);//冒号+3是w的位置开始找/ 
    	string domain = url.substr(pos1 + 3,pos2-(pos1+3));
    	cout << domain << endl;
    	//取路径
    	string uri = url.substr(pos2 + 1);//从域名后的/ +1 的位置找到最后
    	cout << uri << endl;
    
    https
    www.cplusplus.com
    reference/string/string/rfind/
    

string 插入删除

insert插入

string (1)**string& insert (size_t pos, const string& str);
substring (2)string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
c-string (3)string& insert (size_t pos, const char* s);
buffer (4)string& insert (size_t pos, const char* s, size_t n);
fill (5)**string& insert (size_t pos, size_t n, char c); void insert (iterator p, size_t n, char c);
single character (6)**iterator insert (iterator p, char c);
range (7)template <class InputIterator> void insert (iterator p, InputIterator first, InputIterator last);
void test_string5()
{
	string s("hello world");
	s += ' ';//尾插
	s += "!!!";
	cout << endl;
	//头插 效率低 O(N) 尽量少用
	s.insert(0, 1, 'x');//在0的位置插入1个x
	s.insert(s.begin(), 'y');//在头部插入y
	s.insert(0, "test");//在0的位置插入test
	cout << s << endl;
	//中间位置插入,尽量少用
	s.insert(4, "&&&&&&");
	cout << s << endl;
}

erase删除

sequence (1)**string& erase (size_t pos = 0, size_t len = npos);
character (2)iterator erase (iterator p);
range (3) iterator erase (iterator first, iterator last);
void test_string6()
{
	string s("hello world");
	//尽量少用头部和中间的删除,效率低
	s.erase(0,1);//删除头上的一个字符
	s.erase(s.size()-1,1);//删除尾部的一个字符
	cout << s << endl;
    s.erase(3);//从第三个位置 后面全部删除
	cout << s << endl;
}

operator+

字符串相加,返回新的字符串

// concatenating strings
#include <iostream>
#include <string>

main ()
{
  std::string firstlevel ("com");
  std::string secondlevel ("cplusplus");
  std::string scheme ("http://");
  std::string hostname;
  std::string url;

  hostname = "www." + secondlevel + '.' + firstlevel;
  url = scheme + hostname;

  std::cout << url << '\n';

  return 0;
}

输出:http://www.cplusplus.com

getline连续获取一行字符串

方法一的代码就是getline的原理,一个字符一个字符的获取!

(1)istream& getline (istream& is, string& str, char delim);
(2)istream& getline (istream& is, string& str);

字符串里面最后一个单词的长度

#include <iostream>
using namespace std;

int main()
{
    string s;
    //cin>>s;//cin读到空格或换行结束 scanf同理
    //方法一:一个字符一个字符拿
//     char ch = getchar();
//     //char ch = cin.get();
//     while(ch!='\n')
//     {
//         s+=ch;
//         ch = getchar();
//     }
    //方式二:
    getline(cin,s);
    
    size_t pos = s.rfind(' ');
    if(pos == string::npos)
    {
        cout <<s.size()<<endl;
    }
    else{
        cout << s.size() - pos-1;
    }
    return 0;    
}

string 比较大小

relational operators比较大小

(1)bool operator== (const string& lhs, const string& rhs); bool operator== (const char* lhs, const string& rhs); bool operator== (const string& lhs, const char* rhs);
(2)bool operator!= (const string& lhs, const string& rhs); bool operator!= (const char* lhs, const string& rhs); bool operator!= (const string& lhs, const char* rhs);
(3)bool operator< (const string& lhs, const string& rhs); bool operator< (const char* lhs, const string& rhs); bool operator< (const string& lhs, const char* rhs);
(4)bool operator<= (const string& lhs, const string& rhs); bool operator<= (const char* lhs, const string& rhs); bool operator<= (const string& lhs, const char* rhs);
(5)bool operator> (const string& lhs, const string& rhs); bool operator> (const char* lhs, const string& rhs); bool operator> (const string& lhs, const char* rhs);
(6)bool operator>= (const string& lhs, const string& rhs); bool operator>= (const char* lhs, const string& rhs); bool operator>= (const string& lhs, const char* rhs);
void test_string7()
{
	string s1("hello world");
	string s2("string");
	const char* ps = "char";

	cout << (s1 < s2) << endl;//1
	cout << (s1 < ps) << endl;//0
	cout << ("hhh" < s2) << endl;//1
	cout << (s1 < "sss") << endl;//1
}

C++11 补充

stoi :str转int

int stoi (const string&  str, size_t* idx = 0, int base = 10);
int stoi (const wstring& str, size_t* idx = 0, int base = 10);
	int val = stoi("1234");
	cout << val << endl;

to_string :int 转 str

string to_string (int val);
string to_string (long val);
string to_string (long long val);
string to_string (unsigned val);
string to_string (unsigned long val);
string to_string (unsigned long long val);
string to_string (float val);
string to_string (double val);
string to_string (long double val);
	string str = to_string(3.14);
	cout << str << endl;//3.140000

练习题

125. 验证回文串

class Solution {
public:
    bool isLeterOrNumber(char ch)
    {
        if(ch>='0' && ch<='9')
            return true;
        if(ch>= 'a' && ch<='z')
            return true;
        
        if(ch>='A' && ch<= 'Z')
            return true;
        return false;
    }
    bool isPalindrome(string s) {
        int begin = 0,end = s.size()-1;
        while(begin<end)
        {
            while(begin<end && !isLeterOrNumber(s[begin]))
                ++begin;
            while(begin<end && !isLeterOrNumber(s[end]))
                --end;

            if(tolower(s[begin]) != tolower(s[end]))            
                return false;
            ++begin;
            --end;
        }
        return true;
    }
};

415. 字符串相加

image-20220621230055358

class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1 = num1.size()-1;int end2 = num2.size()-1;
        int next = 0;//计算进位
        string retStr;
        while(end1>=0 || end2>=0)
        {
            
            int X1 = 0;
            if(end1>=0)
            {
                X1 = num1[end1]-'0';
                --end1;
            } 
            int X2 = 0;
            if(end2>=0)
            {
                X2 = num2[end2]-'0';
                --end2;
            }
            int retVal = X1 + X2 + next;

            if(retVal > 9)
            {
                next = 1;
                retVal -= 10;
            }
            else
            {
                next = 0;
            }
            
            retStr += retVal+'0';

        } 
        if(next == 1)
        {
            retStr += '1';
        }
        reverse(retStr.begin(),retStr.end());
        return retStr;
    }
};

两个swap的区别?

string::swap:string专属

void swap (string& str);

std::swap:全局

适用于内置类型

template <class T> void swap ( T& a, T& b )
{
  T c(a); a=b; b=c;
}

区别:

string::swap 对于string效率高,只交换资源,改变指针的指向

std::swap 对于string会拷贝构造c (深拷贝1)、a=b(深拷贝2)、b=c(深拷贝3) 进行三次string的深拷贝,代价极高

浅拷贝问题

1.浅拷贝会析构两次

2.期中一个对象进行修改会影响另一个

引用计数(解决浅拷贝)

如果对象不修改,则只增加了引用计数,不进行深拷贝,提高了效率

缺陷:引用计数存在线程安全问题,需要加锁,在多线程环境下要付出代价,在动态库、静态库中有些场景也会存在问题

当一块空间有多个指针指向,则会增加引用计数,例如2个指针指向 ,则引用计数是2

当指针析构的时候,引用计数不是1 则 会减减引用计数,直到只有一个指针的时候才析构

? 也就是说10指针指向a,前9个指着都不会析构a空间,只会减减引用计数,只有最后一个指针会析构a空间

写时拷贝

insert/+=/erase等函数中,先查看引用计数,如果引用计数不是1,要先进性深拷贝,再去修改

当引用计数不是1 ,则进行写实拷贝,因为这块空间不是一个指针维护,所以不能修改

开区间取值

0 - 9 的下标是10个有效字符

所以算有效字符是size - 0 (size是最后一个字符的下一个)

string 补充:vs下

class string
{
private:
	char _Buf[16];	//字符长度小于16,就存在这个数组_Buf中
	char* _Ptr; 	//大于等于16,就会去堆上申请,存在_Ptr中
	size_t _mySize;
	size_t _myRes;
}
sizeof()  //28字节

image-20220701114058992

image-20220701114159833

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 10:32:27  更:2022-07-03 10:33:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:58:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码