C++【泛型编程】【string类常用接口】学习

news2024/9/20 22:56:44

目录

泛型编程

推演实例化

显示实例化

类模板

类模板的声明和定义分离

STL

string 

string的构造和拷贝构造

选取特定字符串拷贝 

 解析:

关于npos的解析

验证

从一个字符串中拷贝前几个字符

解析:

注意:

验证:

size()& length()

string::operator[ ]

迭代器对string进行访问

capacity

capacity的扩容机制

在g++下

 ​编辑

在VS22下

reserve

​编辑

无数据扩容:

在VS22底下运行

在g++底下运行

​编辑

无数据缩容:

在VS22底下

在g++底下 

有数据缩容

在VS22底下

 在g++底下

总结:

resize

重新分配字符串长度

在VS22

 在g++下

resize是既影响空间,又影响数据

operator+= 

insert

 pos位置插入一个实例对象

pos位置插入一个实例对象的子字符串

pos位置直接插入字符串

erase

​编辑

清楚部分字符串

find

 rfine

c_str

substr

find_first_of

getline


泛型编程

泛型编程是跟具体类型无关的代码,关键字template(模板)

什么是模板?

意思就是说写一个swap函数,针对不同的类型,我需要写很多很多种

void swap(int a, int b);
void swap(double a, double b);
void swap(char a, char b);
void swap(short a, short b);
……

有了模板之后,就可以套模板 ,就相当于套公式一样的简单了

模板有推演实例化和显示实例化

推演实例化

template<typename T>
typename 可以改为 class,typename是类型名的意思
void swap(T& a,T& b){
    T temp = a;
    a = b;
    b = temp;
} 
int main() {
    int a = 1, b = 2;
    double c = 1.1, d = 2.2;
    printf("原来我的a是%d,b是%d\n", a, b);
    Swap(a, b);
    printf("现在我的a是%d,b是%d\n", a, b);

    printf("原来我的c是%lf,d是%lf\n", c, d);
    Swap(c, d);
    printf("现在我的c是%lf,d是%lf\n", c, d);
    return 0;
}

 

所以面对要写众多函数,我一个函数模板就搞定了

当然还可以多参数

template<class A,class B>
void fun(A& a, B& b) {
    cout << a << "  " << b << endl;
}

void test2() {
    int a = 1;
    double b = 2.0;
    char c = 'd';
    int* p = &a;
    fun(a, c);
    fun(b, p);
}

int main() {
    test2();
    return 0;
}

给它参数,让模板自己推那是什么吧

以上都是推演实例化

显示实例化

下面看一个返回类型的模板

template<class T>
T* fun1() {
    T* p = new T[10];
    return p;
}

 当我们创建出这个模板的时候,我们就会面临一个问题,就是当我们没有办法去推演这个T应该是什么类型的时候

此刻,推演不了,那我们就要告诉它,它应该返回什么类型的值

void test3() {
    int* ret = fun1<int>();
}

在函数后面告诉这个函数,你应该是个什么样的值,函数名<类型>

当两者功能一样的普通函数和函数模板同时存在的时候

如果参数是普通函数有的,则直接用现成的,如果不是普通参数有的,则会先走函数模板

类模板

类模板都是显示实例化

普通类中,类名就是类型

类模板中,类名就不是类型了,要类名<数据类型>才是整个类的类型

那么为什么要有类模板

当我写一个栈的时候,正常的写法是:

class Stack {
private:
    int* _a;
    int _top;
    int _capacity;
public:
    Stack(int n = 4){
        _a = new int[n];
        _top = 0;
        _capacity = n;
    }

    ~Stack(){
        delete[] _a;
        _a = nullptr;
        _top = _capacity = 0;
    }

};

当我想要构建整型栈的时候很容易,很轻松,直接实例化对象即可

那,当我想要构建 double 栈的时候呢???整型栈类还能派上用场吗,显然是不能的

需要将 int* _a; 改为 double* _a;

需要将 _a = new int [n] 改为 _a = new double [n]; 

这就意味着我要写两个类呀,极其不方便,所以有了类模板

template<class T>
class Stack {
private:
    T* _a;
    int _top;
    int _capacity;
public:
    Stack(int n = 4) {
        cout << "wogouzaole" << endl;
        _a = new T[n];
        _top = 0; 
        _capacity = n;
    }

    ~Stack(){
        cout << "xigou" << endl;
        delete[] _a;
        _a = nullptr;
        _top = _capacity = 0;
    }
};
int main() {
    Stack<int> s1;
    Stack<double> s2;
    return 0;
}

比如Stack<int> 和 Stack<double>是两个不同的类型

他们两个不是同一个类型,即类模板实例化是不同类型,得到的类也是不同的

问题:

那么写出了不同类型的模板后,相应的构造函数是否也需要跟着改变呢?

回答:

我们首先要知道构造函数是函数名跟类名相同,而 Stack<T>是什么,是类型,不是类名

所以构造函数不能够

        Stack<T>() {

        }

而要继续

        Stack(){

        }

类模板的声明和定义分离

1,类模板的函数声明和定义分离要在同一个文件

2,定义要在前头加上类的类型 ,即        类<类型>

template<class T>
class Stack {
private:
    T* _a;
    int _top;
    int _capacity;
public:
    Stack(int n = 4);

    ~Stack();
};

template<class T>
Stack<T>::Stack(int n) {
    cout << "wogouzaole" << endl;
    _a = new T[n];
    _top = 0; 
    _capacity = n;
}

template<class T>
Stack<T>::~Stack() {
    cout << "xigou" << endl;
    delete[] _a;
    _a = nullptr;
    _top = 0;
    _capacity = 0;
}

STL

STL只是C++标准库的其中一部分,STL是标准模板库的缩写,它是C++标准库的重要组成部分

STL数据结构和算法运用较为广泛,下面的string就是STL里面的一个模板之一 

string 

使用string模板的时候需要引入头文件 #include<string>

string的构造和拷贝构造

跟我们平常写的类差不多类型 

	string s1;                //(1)
	string s2("hell0 string");//(4)
	string s3 = s2;           //(2)

选取特定字符串拷贝 

substring (3)

string (const string& str, size_t pos, size_t len = npos);

 解析:

(3) substring constructor

Copies the portion of str that begins at the character position pos and spans len characters (or until the end of str, if either str is too short or if len is string::npos).

从str对象里拷贝一部分字符串,长度从你选取开始的地方到跨越的长度,如果字符串太短或者len  = -1 的话,就直接到末尾

关于npos的解析

npos是size_t 类型的,即无符号整数

而-1是什么,-1在底层的补码形式是 11111111 11111111 11111111 11111111

如果给到无符号整数的话,那么这个值就可以认为其是无穷大的,所以 npos = -1 的时候就可以认为是无限大

验证

从一个字符串中拷贝前几个字符

from sequence (5)
string (const char* s, size_t n);

解析:

Copies the first n characters from the array of characters pointed by s.

注意:

它跟string (const string& str, size_t pos, size_t len = npos);不同的是,它的第一个显示参数是const char* s ,而不是str 

所以如果用str的话,会被当作string (const string& str, size_t pos, size_t len = npos);来执行,执行结果就是从str的第五个位置开始,将此后的所有字符串拷贝,违背了我们想要拷贝前几个字符的初衷

验证:

size()& length()

功能:计算字符串长度,size和length不包含\0

string::operator[ ]

它有两种重载,一种const,一种非const

它的功能是返回的是字符串一个指定位置的引用,用法可以参考数组

	for (size_t i = 0; i < s2.size(); i++) {
		cout << s2[i] << " ";
	}
	cout << endl;

s[i]的本质是s.operator[ ](i)

如果没有const修饰的话,还可以修改:s[i] = 'x';

迭代器对string进行访问

	string::iterator it = s2.begin();
	while (it != s2.end()) {
		cout << *it << " ";
		it++;
	}

iterator 就像是用指针的方式进行遍历,iterator的访问方式是可以线性的访问,在今后的在物理空间上非连续的容器中能够大展身手,基于此特点迭代器是容器的主流访问形式

const_iterator 和 const iterator 有着本质的区别:

const_iterator的本质是保护迭代器指向的数据*it 不被修改

const iterator 的本质是保护迭代器本身不能修改,即 it 不能修改


capacity

capacity的扩容机制

 1 #include <iostream>
 2 #include <string>
 3 using namespace std;
 4 
 5 int main(){
 6   string s1("hello string");
 7   size_t old = s1.capacity();
 8   cout<<old<<endl;
 9   for(size_t i=0;i<1000;i++){                                                                                                                                                       
 10     s1.push_back('x');
 11     if(old!=s1.capacity()){
 12       cout<<s1.capacity()<<endl;
 13       old = s1.capacity();
 14     }
 15   }
 16   return 0;
 17 }

在g++下

 

g++很省,基本上是你字符串有多少就给你多少空间,并且每次达到扩容机制的时候都是2倍2倍的扩,很有规律

在VS22下

capacity在VS22下大概是每次呈1.5倍以此扩容 


reserve

无数据扩容:

确定自己需要多少空间,就可以使用reserve,有时候给多了可能是因为考虑到了对齐规则,不同的平台可能是不同的,扩容机制和空间机制都是一样的,根据平台而定

int main() {
	string s1;
	cout<<s1.capacity()<<endl;

	s1.reserve(100);
	cout << s1.capacity() << endl;

	s1.reserve(200);
	cout << s1.capacity() << endl;


	return 0;
}
在VS22底下运行

在g++底下运行

Linux会老老实实的开

无数据缩容:

在VS22底下

在g++底下 

在无数据地下会缩成我们想要的结果

有数据缩容

在VS22底下
int main() {
	string s1 = "hello reverse";
	cout << "s1的长度是" << s1.size() << endl;
	cout << "s1的原始容量是" << s1.capacity() << endl;

	s1.reserve(10);
	cout << "缩容后s1的长度是" << s1.size() << endl;
	cout << "缩容后s1的长度是" << s1.capacity() << endl;

	return 0;
}

 换成13

它的数据量受到保护不会变,容量也不会变

 在g++底下

总结:

reserve扩容机制可根据自己想要的空间来去指定,但最后得到的空间可能会有多不少,具体看不同的编译器;缩容的时候,会很好的保护数据。


resize

重新分配字符串长度

在VS22
int main() {
	string s1 = "hello resize";
	cout << "s1的长度是" << s1.size() << endl;
	cout << "s1的原始容量是" << s1.capacity() << endl;
	cout << endl;
	s1.resize(100);
	cout << "resize重分配后s1的长度是" << s1.size() << endl;
	cout << "resize重分配后s1的原始容量是" << " "; 
	cout<< s1.capacity() << endl;
	return 0;
}

 resize重新开辟的空间,补充的是'\0'

 在g++下

g++还是一如既往的省吃俭用

resize是既影响空间,又影响数据

可以看出,一旦resize规划的大小比原来小,就会把一些数据除去,留下前几位

>capacity 扩容+尾插

capacity>=resize()>size() 尾插

size()>resize() 容量不变,但数据会直接丢失,即删除数据,保留前n个

resize更多的是开空间和初始化        


operator+= 

想要什么直接在它屁股后面加就好了,不用管那么多的 


insert

 pos位置插入一个实例对象

string& insert (size_t pos, const string& str);

pos位置插入一个实例对象的子字符串

string& insert (size_t pos, const string& str, size_t subpos, size_t sublen); 

Inserts a copy of a substring of str. The substring is the portion of str that begins at the character position subpos and spans sublen characters (or until the end of str, if either str is too short or if sublen is npos).

大致意思就是插入一个实例对象的子字符串,从子字符串的pos位置开始,到某个长度为止

eg:从字符串s1第六个位置插入,选择s2中,从第0个位置开始的后9个字符插入

pos位置直接插入字符串

string& insert (size_t pos, const char* s);
 Inserts a copy of the string formed by the null-terminated character sequence (C-string) pointed by s.

从s字符串中拷贝一份插入到第pos位置

 

 pos位置插入n个连续的字符

string& insert (size_t pos, size_t n, char c);

Inserts n consecutive copies of character c.

还有很多就不一一举例了 


erase

作用就是,清除字符串,减少长度

清楚部分字符串

string& erase (size_t pos = 0, size_t len = npos);


find

find既能找字符串,又能找字符

 

 rfine

与fine查找方向是相反的


c_str

可以使一个实例对象转换为由一个指针指向的字符串数组

一般在文件转换的时候能够使用到

 

int main() {
	string file_name("test.cpp");
	FILE* fout = fopen(file_name.c_str(), "r");
	char ch = fgetc(fout);
	while (ch != EOF) {
		cout << ch;
		ch = fgetc(fout);
	}
}

 


substr

切割字符串 ,参数是起始位置跟长度


find_first_of

Find character in string

Searches the string for the first character that matches any of the characters specified in its arguments.

就是找任何在目标字符串里面的字符

int main ()
{
  std::string str ("Please, replace the vowels in this sentence by asterisks.");
  std::size_t found = str.find_first_of("aeiou");
  while (found!=std::string::npos)
  {
    str[found]='*';
    found=str.find_first_of("aeiou",found+1);
  }
  std::cout << str << '\n';
  return 0;
}
Pl**s*, r*pl*c* th* v*w*ls *n th*s s*nt*nc* by *st*r*sks.

结果就是屏蔽掉了所有有关"aeiou" 

它跟find_last_of 的区别就是,last是反着来找

与之相反的是

find_first_not_of 和 find_last_not_of

 


getline

我们在事件中不免会用上输入输出

如果我们想要对一个string实例化对象s1,进行输入的时候我们会先如何

	string s1;
	cin >> s1;

接着再用cout对其进行打印出来

 

但是我们却发现,只打印出了hello,我的getline却不见了

但我换成这样又能读了

 

但是我的getline却给了s2

这是为何?

在 C++ 中,使用 cin 读取字符串时,如果输入的字符串中包含空格那么 cin 会一直读取到遇到第一个空格为止。因此,当我输入 "hello getline" 时,cin 会首先读取 "hello" 并将其存储在 s1 中。

此时,"getline" 仍然在输入缓冲区中,因为 cin 没有读取它。

接下来,当我尝试读取 s2 时,使用同样的 cin >> s2;cin 会立即从输入缓冲区中读取下一个单词 "getline",因为它是在之前的输入中留下的。这就是为什么不需要再次输入,s2 就已经被赋值了

 因此为了避免这种情况,需要使用getline这个函数接口

此刻便能一整句的将字符串输出来

涉及到两个参数,一个是流提取,另一个是实例化对象 

以上便是本次博文的学习内容了,如有大佬能够指点出论文错误,将不胜感激,谢谢阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1947460.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI应用行业落地100例 | 移民公司Envoy Global引入AI员工赋能,效率飙升80%,开启服务新篇章

《AI应用行业落地100例》专题汇集了人工智能技术在金融、医疗、教育、制造等多个关键行业中的100个实际应用案例&#xff0c;深入剖析了AI如何助力行业创新、提升效率&#xff0c;并预测了技术发展趋势&#xff0c;旨在为行业决策者和创新者提供宝贵的洞察和启发。 Envoy Globa…

Pytorch使用教学2-Tensor的维度

在PyTorch使用的过程中&#xff0c;维度转换一定少不了。而PyTorch中有多种维度形变的方法&#xff0c;我们该在什么场景下使用什么方法呢&#xff1f; 本小节我们使用的张量如下&#xff1a; # 一维向量 t1 torch.tensor((1, 2)) # 二维向量 t2 torch.tensor([[1, 2, 3], …

【Unity PC端打包exe封装一个并添加安装引导】

Unity PC端打包exe封装一个并添加安装引导 比特虫在线制作ico图标ico图标转换工具 选中打包出来的所有文件和ico图标 右键 使用RAR软件 添加到压缩文件 两个名称要相同 设置完点击确认等待压缩完成 然后就可以使用 Smart Install Maker制作引导安装程序了

Matlab进阶绘图第64期—三维分组针状图

三维分组针状图可以看作是三维分组散点图的升级&#xff0c;能够直观地展示各组分、各元素的位置、对比情况。 由于Matlab中未收录三维分组针状图的绘制函数&#xff0c;因此需要大家自行设法解决。 本文使用自制的groupedstem3小工具进行三维分组针状图的绘制&#xff0c;先…

数据结构之深入理解简单选择排序:原理、实现与示例(C,C++)

文章目录 一、简单选择排序原理二、C/C代码实现总结&#xff1a; 在计算机科学中&#xff0c;排序算法是一种非常基础且重要的算法。简单选择排序&#xff08;Selection Sort&#xff09;作为其中的一种&#xff0c;因其实现简单、易于理解而受到许多初学者的喜爱。本文将详细介…

Maven概述

目录 1.Maven简介 2.Maven开发环境搭建 2.1下载Maven服务器 2.2安装&#xff0c;配置Maven 1.配置本地仓库地址 2.配置阿里云镜像地址 2.3在idea中配置maven 2.4在idea中创建maven项目 3.pom.xml配置 1.项目基本信息 2.依赖信息 3.构建信息 4.Maven命令 5.打包Jav…

华杉研发九学习日记17 正则表达式 异常

华杉研发九学习日记17 一&#xff0c;正则表达式 ^ $ 作用&#xff1a; 测试字符串内的模式(匹配) 例如&#xff0c;可以测试输入字符串&#xff0c;以查看字符串内是否出现电话号码模式或信用卡号码模式。这称为数据验证. 替换文本&#xff08;替换》 可以使用正则表达式来…

知识工程经典语言 PROLOG基本介绍

定义 PROLOG语言是一种基于Horn子句的逻辑型程序设计语言&#xff0c;也是一种陈述性语言。 PROLOG的语句 PROLOG语言仅有三种语句&#xff0c;称为事实、规则和问题。 事实 格式 <谓词名>(<项表>). 其中谓词名是以小写英文字母开头的字母、数字、下划线等组成的…

使用js实现常见的数据结构---链表,队列,栈,树

注&#xff1a;本文只作为数据结构的实现参考和个人理解 链表 链表是由多个节点&#xff08;node&#xff09;连接起来的&#xff0c;每个节点包含了一些存储的数据和指向下一个节点的指针&#xff0c; 链表&#xff1a;多个连续的节点构成&#xff0c;节点&#xff1a;包含一…

spring-boot3.x整合Swagger 3 (OpenAPI 3) +knife4j

1.简介 OpenAPI阶段的Swagger也被称为Swagger 3.0。在Swagger 2.0后&#xff0c;Swagger规范正式更名为OpenAPI规范&#xff0c;并且根据OpenAPI规范的版本号进行了更新。因此&#xff0c;Swagger 3.0对应的就是OpenAPI 3.0版本&#xff0c;它是Swagger在OpenAPI阶段推出的一个…

大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

试过可道云teamOS的权限管理,才知道团队协作可以这么顺

在快节奏的工作环境中&#xff0c;团队协作的顺畅与否往往决定了项目的成败。作为团队中的一员&#xff0c;我深知权限管理在团队协作中的重要性。 我们的团队在协作过程中总是被权限问题所困扰。文件共享、资料访问、任务分配……每一个环节都需要小心翼翼地处理权限设置&…

学术研讨 | 区块链与隐私计算领域专用硬件研讨会顺利召开

学术研讨 近日&#xff0c;国家区块链技术创新中心主办&#xff0c;长安链开源社区支持的“区块链与隐私计算领域专用硬件研讨会”顺利召开&#xff0c;会议围绕基于区块链与隐私计算的生成式AI上链、硬件加速、软硬协同等主题展开讨论&#xff0c;来自复旦大学、清华大学、北京…

主题公园- 海豹主题式风格餐厅设计【AIGC应用】

业务背景&#xff1a;海洋馆针对细分客群增设一个打卡主题点位&#xff0c;以海豹主题式餐厅为打卡卖点&#xff0c;效果参见海豹主题式风格。 AIGC概念图制作平台&#xff1a;&#xff08;可灵&#xff09; https://klingai.kuaishou.com/ 关键词&#xff1a; 海豹主题餐厅…

机器学习 | 回归算法原理——随机梯度下降法

Hi&#xff0c;大家好&#xff0c;我是半亩花海。接着上次的多重回归继续更新《白话机器学习的数学》这本书的学习笔记&#xff0c;在此分享随机梯度下降法这一回归算法原理。本章的回归算法原理还是基于《基于广告费预测点击量》项目&#xff0c;欢迎大家交流学习&#xff01;…

Python 教程(二):语法与数据结构

目录 前言专栏列表语法特点实例代码基本数据类型变量命名规则赋值动态类型作用域示例代码 运算符list、set和dict 数据结构 区别1. list&#xff08;列表&#xff09;2. set&#xff08;集合&#xff09;3. dict&#xff08;字典&#xff09; 总结 前言 Python 是一种计算机编…

HarmonyOS实现跨语言交互(Node-API)

Node-API简介 通过Native接口&#xff0c;实现两种代码的交互。 是在Node.js提供的Node-API基础上扩展而来&#xff0c;但与Node.js中的Node-API不完全兼容。本质就是提供了对C/C代码的使用接口&#xff0c;使得两种代码共同工作。规范I/O、CPU密集型、OS底层等能力。 应用场景…

Python | Leetcode Python题解之第264题丑数II

题目&#xff1a; 题解&#xff1a; class Solution:def nthUglyNumber(self, n: int) -> int:dp [0] * (n 1)dp[1] 1p2 p3 p5 1for i in range(2, n 1):num2, num3, num5 dp[p2] * 2, dp[p3] * 3, dp[p5] * 5dp[i] min(num2, num3, num5)if dp[i] num2:p2 1if …

pytest的安装和介绍和 Exit Code 含义

pytest 准备工作&#xff08;在cmd里&#xff09;&#xff1a; 1安装 pip install -U pytest2验证安装 pytest --version # 会展示当前已安装版本3其他的 显示可用的内置函数参数 pytest --fixtures通过命令行查看帮助信息及配置文件选项 pytest --help一、pytets框架中的…

Windows安装go语言开发环境

一、下载安装包 安装包下载地址 下载完毕后双击进行安装。 查看是否安装成功&#xff1a; go version #查看go版本 go env #查看go环境变量正常显示则安装完成。 二、安装vscode 一般开发go语言项目使用vscode工具&#xff1a; 下载地址 下载完毕后双击进行安装。 三…