C++模板基础和STL之string

news2025/2/24 8:59:36

泛型编程之模板

使用实际调用的函数不是同一个,因为不同类型参数,函数栈帧中开辟的空间不一样。参数不一样,所以调用的函数也不一样。使用模板速度必重载速度更快,因为是编译器直接生成。而STL就叫标准模板库。

template<class T>

void Swap(T& x1, T& x2) // 本意是用引用&
{
    T x = x1;
    x1 = x2;
    x2 = x;
}
  • 特别场景:当函数像求和一样,两个都用模板的T类型,那么T不可以同时为doble和int,如何解决这种问题?
      答:显示实例化。这种方式是指定模板参数T的具体类型。

  • 对于模板参数T:

可以做参数、也可能做返回值。
typedef中可以使用参数T使得某个类或某类数据结构支持尽可能多的参数类型,而只是用typedef int datatype不能同时满足两种或以上的数据结构存储的数据类型。实际上,面对不同参数,编译器会自动生成不一样的代码,不必自己写出来重复度极高的相似类或结构体的代码,工作都交给了编译器。如下写法:

stack<int> st1;
stack<double>st2;
stack<int*>st4;

以上,编译器实例化出几份代码分别是存int、double的等几个存不同元素类型的栈。

  • 注意点:
  1. 如果在类外使用类模板写函数,还要重声明使用了类模板T。不然如下第二行的T识别不出来。
    请添加图片描述
  2. 使用类模板后,类型变成了class_name,class_name只是类名。
  • 模板实例化:
    隐式实例化和显示实例化:
      答:隐式是直接传参,让编译器自己判断,显示是函数名后:<>显示实例化。模板函数和非模板函数(平时常用的)可以同时存在,调用时候,如果显示给类型,会优先调用非模板函数。隐式调用,优先调用模板函数。此外,如果模板可以产生更匹配的函数,就选择模板函数。模板不允许自动类型转换,但是普通函数可以自动类型转换。
  • 类模板:
    比如一个项目中,我们如果定义栈的类,内部类型使用了class T。那么在同一项目中,可以给stack存各种类型数值,而不用类模板,就肯定不行,只能存单一类型数值。

关于模板的疑问点:

  1. 下面有关C++中为什么用模板类的原因,描述错误的是? ( )
    A.可用来创建动态增长和减小的数据结构
    B.它是类型无关的,因此具有很高的可复用性
    C.它运行时检查数据类型,保证了类型安全
    D.它是平台无关的,可移植性
  • 问题:D对吗?C对吗?
    模板运行时不检查数据类型,也不保证类型安全,相当于类型的宏替换。
    只要支持模板语法,模板的代码就是可移植的,总之它不安全,且如果支持模板语法,就可移植。
  1. 在下列对fun的调用中,错误的是( )
    template

T fun(T x,T y){

return xx+yy;

}

A.fun(1, 2)
B.fun(1.0, 2)
C.fun(2.0, 1.0)
D.fun(1, 2.0)

  • B、D哪个错了?
    显示实例化,改变的是传入的参数类型, 而不是说确定了模板中T类型,所以D会都变float,D正确了。
  1. 下列关于模板的说法正确的是( )
    A.模板的实参在任何时候都可以省略
    B.类模板与模板类所指的是同一概念
    C.类模板的参数必须是虚拟类型的
    D.类模板中的成员函数全是模板函数
  • 我全不懂
    A 不一定有时候需要指定
    B 模板类是通过类模板实例化的具体类,不一样。也就是说:模板类更小,是个具体的。类模板是说你在使用类模板
    C 错误,不是虚拟,也可以直接传,如2题的D
    D 对,因为数据成员是模板类型,所以函数都是模板函数。
  1. 下列的模板声明中,其中几个是正确的( )

1)template

2)template<T1,T2>

3)template<class T1,T2>

4)template<class T1,class T2>

5)template<typename T1,T2>

6)template<typename T1,typename T2>

7)template<class T1,typename T2>

8)<typename T1,class T2>

  • 2、4、6 7
    class可以用typename代替。
  1. 下列描述错误的是( )
    A.编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础
    B.函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具
    C.模板分为函数模板和类模板
    D. 模板类跟普通类以一样的,编译器对它的处理时一样的
    D错误,肯定不一样。

STL

  STL是C++标准模板库(standard template libaray)。此外STL版本一般一致,常见有PJ、SGI版本等。
核心:容器、算法

string

  1. 基本知识点:
    必须导入包,因为<<、>>对string类型操作需要重载。
    构造函数7、成员函数上百,只需要掌握常用即可。
    请添加图片描述
  2. 常见函数:
  • size()和length()结果一样,但是推荐size。它俩的计算都不包含\0。此外由于其它STL容器中习惯用size,因为只有字符串习惯说length长度,多用size()为了保持统一,方便记忆用size。
  1. 对capacity()的了解:查看string容量的函数
      答:利用s += ‘char’ 或其它函数会使得字符串增容,字符串变量长度发生改变后会自动增容。大小包括**\0**。
  2. 当字符串长度变化后如何增容的?
      答:如果字符串变量长度改变后,开始会再malloc()一个32的空间,此后如果长度再变化,会成1.5倍地做扩容。
  3. 访问之operator[]
  • std::string::operator[]
      使得string能直接通过s[i]得到下标为i的字符值。返回类型是引用,出了范围还存在,所以可以直接返回私有这个成员
    请添加图片描述
      原理:重载[],通过【pos】直接返回字符指针_str[pos]。以底层的私有指针变量为一维数组返回下标为pos的_str。即使是私有成员变量也可以返回,可以返回它的地址,也可以返回它的拷贝值。
  • 此外,at()功能也类似[],但是它抛出异常,而不是断言。
  • 字符串拼接:
      声明string s而不做初始化也可以任意拼接字符串,但是建议像做leetcode,结果字符串还是以""初始化吧。

遍历string之迭代器

  1. 理解:
      暂时认为是指针
  2. 使用代码:
    需要写++it。
    请添加图片描述
  3. 迭代器变量的操作:通过it可以访问string某个位置的值,也可以通过it改字符串中字符。*it += 1;
  4. 反向迭代器:rbegin()、rend()
      与之前正向迭代器操作一样,需要加上r。结果方向相反
      它的rbegin()指向’\0’的前一个。rend()指向第一个字符。
    请添加图片描述
  • 既然有了下标搭配[]的用法,为什么还要迭代器呢?
      因为其它STL也需要遍历,不能通过[]方式,为了保持统一

  • 这里发现:创建正、反迭代器类型it、rit都很费劲,右值是.begin()或.end(),用auto可以自动识别右值类型做转换。

  • 当迭代器遇上const常字符串的引用:不能修改,只能*it读。

遍历string之范围for

  1. 范围for运行效果如上述迭代器。
for(auto e: s1)
	cout<<e<<endl;
  1. 使用:
  • 可以读写:
  1. auto e只是对s1中的值做拷贝。如果要修改字符串,应该使用引用。
  2. 规定&必须放在e前面。
  3. 如下的auto可换char,但是不如auto好用。
  1. 代码:
for(auto& e: s1)
	cout<<e<<endl;

reserve():专门的字符串扩容函数,以’\0’填充

  • 作用:
  1. 开空间: 申请1000个空间,一次多开,会让string减少增容次数,提高效率。
    s.reserve(1000);
  2. 开空间加初始化:reserve(100, ‘x’);
    开100个空间,每个位置填’x’。
    此外,reserve扩容并不会影响原来的string长度。
    常使用reserve()而不是resize()

resize()

  可以扩容,也可以缩容。缩容可能让字符串丢失。

c_str()

  1. 解释:
    获取C形式的string,以’\0’结束。
    通过字符串变量可以直接调到。
  2. 用法:
    可以使用在字符串读文件上:
      如下第一种不可编译,因为fopen()第一个参数是const char*,而这里的file是string。使用file_str()是const char*。
string file("test.txt");
FILE* fout = fopen(file, "W");
-------------下面可以的-----------------
string file2(test.txt);
FILE* fout = fopen(file_str, "W");

substr()

截取字符串,返回的结果是深拷贝。

find()

  • 功能:
      默认从参数size_t pos=0开始找。如果找不到,给整型最大值。 string::npos,npos是string中的静态变量。
  • 使用技巧:
      用find(‘str’)求字符位置,再搭配substr()截取子串。substr(0, pos-0),技巧就是用find()结果减某个位置

rfind()

  • 情景:文件名是:.txt.zip,我们找.zip,如果寻找策略是从第一个".“到文件末尾寻找”.zip",那么只能得到".txt.zip",只需要".zip"这需要从右往左找。用rfind(“.”)得到靠后的"."位置,然后用substr(pos)得到从这个位置到最末尾的子串。
    请添加图片描述

url.substr():寻找从某个位置起往后的n个。

s.substr(x, n):从下标为x起找n个。

erase():删除

param1:起始位置
param2:删个数。如果不给,就删完。

  • 删头:
    uri.erase(0, 1);
  • 删尾:如果删除位置是末尾,则倒着删
    uri.erase(uri.size() - 1, 1);
  • 使用erase()对字符串去头尾:
      搭配find_first_not_of()、find_last_not_of(" “),后面这个意思是最后一个不是空格,所以还应该+1,才是末尾空格位置。以下” "或’ '都可以用
// 不空才去做
// find_first_not_of
void trimSpace(string& s)
{
    if (!s.empty())
    {
        s.erase(0, s.find_first_not_of(" "));
        s.erase(s.find_last_not_of(" ") + 1);

    }
}

sto()系列和to_string()

数字变字符串、字符串变数字:
stoi()转为int类型,stold转为double类型。
请添加图片描述

demo:

  1. 解析url:
int main()
{
    // s1 末尾默认有\0
    string url = "https://www.baidu.com/?tn=62095104_31_oem_dg/";
    size_t pos1 = url.find(":");
    string protocol = url.substr(0, pos1 - 0);
    cout << "协议:"<<protocol<< endl;
    // 找第二个'/',find()和rfind()直接用都拿不到。
    // 这就可以用find的其它用法:从某个位置开始找,我们根据url的规律,可以从w开始,pos1+3
    size_t pos2 = url.find('/', pos1+3);
    // cout <<"第二个/位置" << pos2 << endl;  pos2 = 21
    string domain = url.substr(pos1+3, pos2 - (pos1+3));
    cout << "域名:"<<domain<<endl;
    string uri = url.substr(pos2 + 1);
    cout << "区分段:" << uri << endl;
    // 删头
    uri.erase(0, 1);
    cout << "区分段:" << uri << endl;
    // 删尾:从位置起删,如果是最后位置,则反着删
    uri.erase(uri.size() - 1, 1);
    cout << "区分段:" << uri << endl;

    return 0;
}

  1. 读空格后单词长度
#include <iostream>
#include<string>
#include<string>
using namespace std;

int main() {
    string s;
    //char ch = getchar();
    // 单个字符接收 以'\n'为结尾 会忽视空格
    /*while (ch != '\n')
    {
        s += ch;
        ch = getchar();
    }*/
    getline(cin, s);
    int pos = s.rfind(' ');
    string res = s.substr(pos + 1);
    cout << res.size() << endl;
    return 0;
}

字符串的接收:

cin>>s,
cin和scanf字符串默认以空格和换行结束
当要输入字符串以空格间隔,只接收第一个。

  • 两种接收带空格字符串的方式:
  1. 循环getchar()方式
string s;
char ch = getchar();
// 单个字符接收 以'\n'为结尾 会忽视空格
    while (ch != '\n')
    {
        s += ch;
        ch = getchar();
    }
  1. getline()
getline(cin, s);

练习

  1. leetcode344. 反转字符串
  • 思路:
       利用左右变量begin、end,双重循环,依次交换头尾。外圈要符合begin<end,内部判断左右两端是字母就停下做交换。不用担心不够两个的情况,如果最后指向同一个位置,那么交换自己之后就结束,且记得交换完,begin++,end–

  • 注意:

  1. C++不用写swap,库中提供了。
  2. 像这种都是正数的变量如下标begin、end且也较小,用size_t无符号整形即可。
bool isLetter(char ch)
{
    if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
        return true;
    else
        return false;
}

class Solution {
public:
    string reverseOnlyLetters(string s) {
        size_t begin = 0;
        size_t end = s.size() - 1;
        // 左到右判断一遍 遇到字母就停下 都是字母才交换。
        // 注意换完还得往后走
        while (begin < end)
        {
            while (begin < end && !isLetter(s[begin]))
            {
                begin++;
            }
            while (begin < end && !isLetter(s[end]))
            {
                end--;
            }1
            swap(s[begin], s[end]);
            begin++;
            end--;
        }
        return s;
        }
};
  1. leetcode 387. 字符串中的第一个唯一字符
  • 思路:
      利用计数排序思想,字母范围比较集中,st[i] - ‘a’ 结果为0~27,最后遍历寻找为1的值。

  • 报错:
    请添加图片描述
    一直报错无返回值,仔细看因为左边提示:会有-1的结果。所以没有认真看题,不是所有都符合规范

  • 代码

class Solution {
public:
    int firstUniqChar(string s) {
        int countArr[26] = { 0 };
        // 统计次数
        for (size_t i = 0; i < s.size(); ++i)
            countArr[s[i] - 'a']++;
        for (int j = 0;j < s.size(); j++)
            if (countArr[s[j] - 'a'] == 1)
                return j;
        return -1;
    }
};
  • 巧妙点和习惯:
  1.   要返回原始字符串中的位置, s[j] - ‘a’ 得到字母表中第几个值。其中j,就是该字母在原始string中位置的记录。
  2. 会使用 size_t:当数字范围较小且都是正数,尽管使用。
  3. string习惯使用size()求长度。

  1. 回文串
    这个题让看字母类和数字类是否相同,不考虑其他特殊字符。
  • 思路:
  1. 双指针法:begin、end同时挪动,是
    为什么是两层:移动过程可能会停下做操作,做完操作后,因为外层循环使得继续去移动。所以两层循环搭配双指针非常合适。
    2. 巧妙用法:大小写转换:对数字做tolower()和对字母做tolower()效果一样,不会影响数字,且比较字母更加方便。
  2. 注意事项:当前都是字母后,比较完应该挪动指针
class Solution {
public:
    bool isLetterOrNum(char ch)
    {
        if (ch >= '0' && ch <= '9')
            return true;
        if ((ch >= 'a' && ch <= 'z') || (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 && !isLetterOrNum(s[begin]))
            {
                begin++;
            }
            while (begin < end && !isLetterOrNum(s[end]))
            {
                end--;
            }
            if (tolower(s[begin])!=tolower(s[end]))
            {
                cout << "这里不对" << endl;
                return false;
            }

            else {
                end--;
                begin++;
            }
        }
        return true;
    }
};
  1. leetcode415. 字符串相加
    主要是长测试用例的相加:
  • 对于leetcode类测试:
    如下代码:
    上面的可以,为什么下面不可以?
Solution s1 = Solution();
-----------上面的可以,为什么下面不可以----------------
Solution s1 = new Solution();
-----------下面的又可以----------------
Solution* s1 = new Solution();

因为new在申请空间,返回地址,所以用类指针类型接收。
习惯上,操纵类喜欢用指针

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

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

相关文章

Activity的生命周期

文章目录Activity的生命周期一.返回栈二.Activity状态1.运行状态2.暂停状态3.停止状态4.销毁状态三.Activity的生存期onCreate()onStart()onResume()onPause()onStop()onDestroy()onRestart()完整生存期可见生存期前台生存期Activity的生命周期图体验Activity的生命周期编写三个…

PTA题目 阅览室

天梯图书阅览室请你编写一个简单的图书借阅统计程序。当读者借书时&#xff0c;管理员输入书号并按下S键&#xff0c;程序开始计时&#xff1b;当读者还书时&#xff0c;管理员输入书号并按下E键&#xff0c;程序结束计时。书号为不超过1000的正整数。当管理员将0作为书号输入时…

机器人工程考研难易主观感受和客观数据

简易版&#xff08;共性&#xff09;&#xff1a; 主观反馈&#xff1a;越来越难 客观数据&#xff1a;研究生录取人数越来越多&#xff0c;比例也越来越高 看起来矛盾&#xff0c;其实并非如此。 详细数据&#xff1a; 80后是指1980年至1989年出生的人口&#xff0c;对应2…

MySQL_07:单行函数

文章目录一、函数的基本理解1.函数的理解1.1内置函数的基本理解1.2不同DBMS函数的差异1.3MySQL的内置函数的分类2.图解多行函数和单行函数二、单行函数1.数值函数1.1基本函数1.2角度与弧度互换函数1.3三角函数1.4指数与对数1.5进制间转换2.字符串函数3.日期和时间函数3.1获取日…

python常用操作之使用多个界定符(分隔符)分割字符串

本系列文章会总结python中各种常见及常用的内置方法、对不同数据结构的操作&#xff0c;参考书籍《python cookbook》第三版 分割字符串单个界定符分割字符串代码演示多个界定符分割字符串代码演示注意总结在日常学习及工作中&#xff0c;不论是在解析数据还是在产出输出&#…

【LeetCode】No.98. Validate Binary Search Tree -- Java Version

题目链接&#xff1a;https://leetcode.com/problems/validate-binary-search-tree/ 1. 题目介绍&#xff08;Validate Binary Search Tree&#xff09; Given the root of a binary tree, determine if it is a valid binary search tree (BST). 【Translate】&#xff1a; 给…

Qt 堆栈窗体QStackedWidget使用

QStackedWidget控件相当于一个容器&#xff0c;提供一个空间来存放一系列的控件&#xff0c;并且每次只能有一个控件是可见的&#xff0c;即被设置为当前的控件。QStackedWidget可用于创建类似于QTabWidget提供的用户界面。 它是一个构建在QStackedLayout类之上的方便布局小部件…

dolphinscheduler 2.0.6 负载均衡源码

目录&#x1f42c;官网介绍&#x1f42c;负载均衡&#x1f420;加权随机&#xff08;random&#xff09;&#x1f420;平滑轮询&#xff08;roundrobin&#xff09;&#x1f420;线性负载&#xff08;lowerweight&#xff09;&#x1f435;其它&#x1f42c;官网介绍 官网资料&…

【计算机网络】网络层:虚拟专用网

由于IP地址的紧缺&#xff0c;一个机构能够申请到的IP地址数往往远小于本机构拥有的主机数。 如果一个机构内部的计算机通信也采用TCP/IP协议&#xff0c;那么这些仅字机构内部使用的计算机就可以由本机构自行分配其IP地址。 本地地址&#xff08;专用地址&#xff0c;互联网对…

Java培训教程给bean的属性赋值

依赖注入的方式 1. 通过bean的setXxx()方法赋值 Hello World中使用的就是这种方式 2. 通过bean的构造器赋值 Spring自动匹配合适的构造器<bean id“book” class“com.atguigu.spring.bean.Book” > <constructor-arg value “10010”/> …

有位p8终于把珍藏多年的算法视频给分享出来了,总共3.81G

大厂面试都开始问算法了&#xff0c;要是你不会算法只能与大厂失之交臂。为了解决大家算法方面的缺失&#xff0c;小编特此分享算法的学习路线和学习视频&#xff0c;希望大家能够喜欢&#xff01;&#xff01;&#xff01; 左神算法-KMP算法及其扩展 左神算法-Morris遍历及其…

S2B2C供应链系统将引领商业模式!S2B2C供应链电商系统实现订单管理数智化

近年来随着5G、人工智能、区块链、大数据、物联网等技术的快速发展&#xff0c;带动了数字经济的升级发展&#xff0c;疫情的不断反复更是加速推动了数字经济新发展&#xff0c;产业数字化和数字产业化进程加快&#xff0c;促进了各行各业快速发展&#xff0c;S2B2C供应链系统模…

17【redux】

17 【redux】 引言 我们现在开始学习了 Redux &#xff0c;在我们之前写的案例当中&#xff0c;我们对于状态的管理&#xff0c;都是通过 state 来实现的&#xff0c;比如&#xff0c;我们在给兄弟组件传递数据时&#xff0c;需要先将数据传递给父组件&#xff0c;再由父组件…

[附源码]Python计算机毕业设计大学生志愿者管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

软件工程经济学期末复习

1、利润 收入-成本费用 2、资产 流动资产非流动资产 3、显性成本可以用货币计量&#xff0c;是可以在会计的帐目上反映出来的 4、领取什么保险应缴纳个人所得税 商业保险 某企业一项固定资产的原价为800 0000元&#xff0c;预计使用年限为6年&#xff0c;预计净残值为5 0…

软件测试linux面试相关的知识

一、常用的命令 ls&#xff08;查看目录下的内容&#xff0c;-a显示隐藏目录&#xff09; cd&#xff08;进入某个目录&#xff0c;cd .. 返回上一层目录&#xff0c;cd - 返回上一次的目录&#xff0c;cd / 返回根目录&#xff09; pwd&#xff08;显示当前绝对路径&#x…

阿里云易立:以增效促降本,容器服务全面进入智能化时代

容器技术已经跨越鸿沟&#xff0c;广泛应用于金融、通讯、制造、交通等千行百业。Kubernetes支撑的工作负载也从早期单一的互联网应用发展到数据库、AI、大数据等等&#xff0c;并覆盖了公共云、专有云、边缘云等多样化、动态的云环境。 11月5日&#xff0c;2022杭州 云栖大会…

在Windows7在部署Hadoop+Hbase

0. 准备工作 0.1 电脑上现在没有jdk 0.1 提前准备好文件 1. 现在开始安装jdk 1.8.0_60 安装成功&#xff0c;没啥问题 小疑问&#xff1a;自动配置好了环境变量? 1.1 小记 在安装jdk的时候&#xff0c;有三种小工具&#xff0c;可以根据需要选择性安装 JDKjre源代码 虽…

牛客网语法篇练习基础语法(二)

1.牛牛正在给他的朋友们买电影票&#xff0c;已知一张电影票价是100元&#xff0c;计算 x 位朋友的总票价是多少&#xff1f; x int(input()) a x*100 print(a) 2.给定两个整数a和b (0 < a,b < 10,000)&#xff0c;计算a除以b的整数商和余数。 a,b map(int,input().…

【深入浅出Java并发编程指南】「剖析篇」Fork/Join框架的实战开发和原理探究指南

前提概述 Java 7开始引入了一种新的Fork/Join线程池&#xff0c;它可以执行一种特殊的任务&#xff1a;把一个大任务拆成多个小任务并行执行。 我们举个例子&#xff1a;如果要计算一个超大数组的和&#xff0c;最简单的做法是用一个循环在一个线程内完成&#xff1a; 算法原理…