C++-模板与容器

1、模板

模板可以让类或者函数支持一种通用类型,这种通用类型在实际运行过程中可以使用任何数据类型。因此程序员可以写出一些与类型无关的代码。这种编程方式也叫“泛型编程”。

通常有两种形式:

  • 函数模板
  • 类模板

1.1 函数模板

//模板类型声明
template<class T>//T 模板类型

是让一个函数支持模板编程,可以使函数支持通用数据类型。

#include <iostream>
#include <string.h>
using namespace std;
// 模板函数
template<class T>//T 模板类型
add(T a,T b)
{return a+b;
}
int main()
{
    cout << add(1,2) << endl; // 3
    string s1 = "hello";
    string s2 = "world";
    cout << add(s1,s2) << endl; // helloworld
    cout << add(2.3,23.12) << endl;
//  cout << add(1,23.3) << endl; 
//编译器自动识别数据类型
//错误,无法推导,局限性:只能用同一种类型
//错误,两个指针无法相加,如果想要实现这个功能,想要将+运算符重载
//cout << add("hello","world") << endl;return 0;
}

1.2 类模板

使一个类支持模板编程,可以使类支持通用数据类型。

作用域:只有一个花括号大小

#include <iostream>
#include <string.h>
using namespace std;
// 模板函数
//template<typename T>
//T add(T a,T b)
//{
//   return a+b;
//}
template<class T>    // 可以class 也可以typename
class Test
{
private:
    T val;
public:Test(T v){
        val = v;}
    T get_val(){return val;}void set_val(T v){
        val = v;}
};
int main()
{// 类模板需要执行模板类型,通过<>。只要在之后的程序中看到<>,肯定是个模板类
    Test<int> t1(20);
    cout << t1.get_val() << endl;
    t1.set_val(10);
    cout << t1.get_val() << endl;
    t1.set_val(2.34);
//    cout << t1.get_val() << endl; // 数据窄化 输出2
    Test<double> t2(2.14); // 指定类模板为double
    cout << t2.get_val() << endl;
    t2.set_val(3);
    cout << t2.get_val() << endl;return 0;
}

类内定义,类外声明

上面的写法可以改为类内声明类外定义的形式:

#include <iostream>
#include <string.h>
using namespace std;
template<class T>    // 可以class 也可以typename
class Test
{
private:
    T val;
public:Test(T v){
        val = v;}
    T get_val();//类内定义 类外声明void set_val(T v);
};template<class T>
Test<T>::get_val()//类内定义 类外声明
{return val;
}template<class T>
//返回值类型 模板类型作用域 函数名(传参)
void Test<T>::set_val(T v)
{
    val = v;
}int main()
{
    Test<int> t1(20);
    cout << t1.get_val() << endl;
    t1.set_val(10);
    cout << t1.get_val() << endl;
    t1.set_val(2.34);
//    cout << t1.get_val() << endl; // 数据窄化输出2
    Test<double> t2(2.14);
    cout << t2.get_val() << endl;
    t2.set_val(3);
    cout << t2.get_val() << endl;return 0;
}

2、容器

2.1 标准模板库STL

标准模板库(Standard Template Library,STL)是惠普实验室开发的一系列软件的统称。虽然说它主要出现在C++中,但是它在被引入C++之前,该技术就已经存在很长时间了。

STL的代码从广义上讲分为三部分,algorithm(算法)、container(容器)、iterator(迭代器)。几乎所有的代码都采用了模板类和模板函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用的机会。

2.2 概念

容器是用来存储数据的集合,数据元素可以是任何类型(因为是使用模板实现的)

容器类的使用,都要引入对应的头文件。

2.3 顺序容器

顺序容器中每个元素均有固定的位置并呈线性排布。除非使用删除或者是插入操作改变元素的位置。

2.3.1 array数组

array是C++11新增的容器类型,与传统数组相比更加安全,易于使用。array数组是定长的。没法方便的伸缩。array创建数组必须是定长的,且不能用变量定长。

QMAKE_CXXFLAGS += -std=c++11

array数组常用函数

#include <array> // 头文件
array<int,5> arr = {1,2,3}; // 后面两位补零

#include <iostream>
#include <array> // 头文件
using namespace std;
int main()
{// 创建一个长度为5的int数组
    array<int,5> arr = {1,2,3}; // 后面两位补零
    cout << arr[0] << endl; // 1
    cout << arr[4] << endl; // 0
    cout << arr.at(2) << endl; // 3 推荐使用at
    arr[3] = 200;// forfor(int i = 0; i < arr.size(); ++i){
        cout << arr.at(i) << " ";}
    cout << endl;// for eachfor(int i:arr){
        cout << i << " ";}
    cout << endl;//迭代器遍历,后面讲// TODOreturn 0;
}

2.3.2 vector 向量

vector向量内部是由数组实现的,比较适合进行随机的存取操作,不擅长插入删除操作。

不是定长,方便伸缩。(大量数据存取时且不经常插入删除)

vector向量常用函数

#include <vector> // 头文件
vector<int> vec(50);//创建一个大小为5类型为int,默认值为0的vector向量数组
vec.push_back(222);//在最后面追加新元素
vec.insert(vec.begin()+2,333);//begin()指向第一个元素位置
vec.pop_back();  // 删除最后一个元素
vec.erase(vec.end()-2);//end()指向最后一个元素后面,删除了倒数第二个元素
vec.empty()// 判断是否为空,0非空,1空
vec.clear();// 清空

#include <iostream>
#include <vector> // 头文件
using namespace std;
int main()
{
//    vector<int> vec(5,0);//创建一个大小为5类型为int,默认值为0的vector向量数组
//    cout << vec.size() << endl;
//    for(int i = 0; i < vec.size(); ++i)
//    {
//        cout << vec[i] << endl;
//    }
    vector<int> vec = {1,2,3};// 增
    vec.push_back(222);//在最后面追加新元素for(int i :vec){
        cout << i <<" ";}
    cout << endl;
    cout<<"--------------------"<<endl;
    cout <<"向量大小:"<< vec.size() << endl;// 插入操作,定向插入// begin()可以返回指向第一个元素的迭代器指针,+2是在第三个位置上插入333
    vec.insert(vec.begin()+2,333);//begin()指向第一个元素位置
    cout<<"定向插入:"<<endl;for(int i :vec){
        cout << i <<" ";}
    cout << endl;
    cout<<"--------------------"<<endl;// 删// 删除最后一个元素
    cout<<"删除最后一个元素:"<<endl;
    vec.pop_back();for(int i :vec){
        cout << i <<" ";}
    cout << endl;
    cout<<"--------------------"<<endl;// 删除了第二个元素
    cout<<"删除了第二个元素:"<<endl;
    vec.erase(vec.begin()+1);for(int i :vec){
        cout << i <<" ";}
    cout << endl;
    cout<<"--------------------"<<endl;// 删除了倒数第二个元素
    cout<<"删除了倒数第二个元素:"<<endl;
    vec.erase(vec.end()-2);//end()指向最后一个元素后面for(int i :vec){
        cout << i <<" ";}
    cout << endl;
    cout<<"--------------------"<<endl;// 改
    cout<<"改:"<<endl;
    vec[1] = 666;
    vec.at(0) = 888;for(int i :vec){
        cout << i <<" ";}
    cout << endl;
    cout<<"--------------------"<<endl;// 查
    cout << vec[1] << endl;for(int i :vec){
        cout << i <<" ";}
    cout << endl;
    cout << "---------------------" <<  endl;for(int i = 0; i < vec.size(); ++i){
        cout << vec[i] << " ";}
    cout <<  endl;// 判断是否为空,0非空,1空
    cout << vec.empty() << endl;// 清空
    vec.clear();
    cout << vec.empty() << endl;// 迭代器遍历先省略return 0;
}

2.3.3 list列表

list内部是由双向循环链表实现的。不支持下标。优点:可以高效的删除和插入操作。但不适合随机的存取操作。

list列表常用函数

#include <list> // 头文件
list<string> lis2{"hello","world"};
//创建了一个长度为2的列表,第一个元素就是hello,第二个world
list<string> lis(5,"hello");
// 创建一个长度为5的列表,每个元素都是“hello”
lis.push_back("world"); // 向后追加一个单元素
lis.push_front("hahaha");   // 向前追加一个单元素
lis.insert(++lis.begin(),"2222"); // 在第二个位置上插入“2222”,lis.begin()只能自增,不能加数值
lis.pop_back(); // 删最后一个元素
lis.pop_front();    // 删除第一个元素
lis.push_back("world"); // 向后追加一个单元素
list<string>::iterator iter = lis.begin();// 保存迭代器指针
advance(iter,1); // 移动迭代器指针

#include <iostream>
#include <list> // 头文件
using namespace std;
int main()
{// 创建了一个默认无数值的list
    list<string> lis1;//创建了一个长度为2的列表,第一个元素就是hello,第二个world
    list<string> lis2{"hello","world"};for(string s:lis2){
        cout << s << " ";}
    cout <<endl;// 创建一个长度为5的列表,每个元素都是“hello”
    list<string> lis(5,"hello");
    cout<<"创建一个长度为5的列表,每个元素都是“hello”"<<endl;for(string s:lis){
        cout << s << " ";}
    cout<<endl;
    cout<<"___________________"<<endl;// 增
    lis.push_back("world"); // 向后追加一个单元素
    cout<<"向后追加一个单元素"<<endl;for(string s:lis){
        cout << s << " ";}
    cout<<endl;
    cout<<"___________________"<<endl;    lis.push_front("hahaha");   // 向前追加一个单元素
    cout<<"向前追加一个单元素"<<endl;for(string s:lis){
        cout << s << " ";}
    cout<<endl;
    cout<<"___________________"<<endl;    lis.insert(++lis.begin(),"2222"); // 在第二个位置上插入“2222”,lis.begin()只能自增,不能加数值
    cout<<"在第二个位置上插入“2222”"<<endl;for(string s:lis){
        cout << s << " ";}
    cout<<endl;
    cout<<"___________________"<<endl;// 删
    lis.pop_back(); // 删最后一个元素
    cout<<"删最后一个元素"<<endl;for(string s:lis){
        cout << s << " ";}
    cout<<endl;
    cout<<"___________________"<<endl;    lis.pop_front();    // 删除第一个元素
    cout<<"删除第一个元素"<<endl;for(string s:lis){
        cout << s << " ";}
    cout<<endl;
    cout<<"___________________"<<endl;    lis.push_back("world"); // 向后追加一个单元素
    cout<<"删除第一个元素"<<endl;for(string s:lis){
        cout << s << " ";}
    cout<<endl;
    cout<<"___________________"<<endl;// 保存迭代器指针
    list<string>::iterator iter = lis.begin();advance(iter,1); // 移动迭代器指针
    lis.insert(iter,"3333");  // 插入3333
    cout<<"通过移动迭代器指针定向插入,插入3333"<<endl;for(string s:lis){
        cout << s << " ";}
    cout<<endl;
    cout<<"___________________"<<endl;// 删除最后一个元素
    iter = lis.end();
    iter--;
    lis.erase(iter);//删除iter指针指向的元素
    cout<<"通过移动迭代器指针定向删除"<<endl;for(string s:lis){
        cout << s << " ";}
    cout<<endl;
    cout<<"___________________"<<endl;// 返回第一个元素的引用
    cout << "打印第一个元素* = " << lis.front() << endl;// 返回最后一个元素
    cout << "打印最后一个元素* = " << lis.back() << endl;// 改
    iter = lis.end();advance(iter,2);//将迭代器指针移动到第二个位置*iter = "200";
    cout<<"通过移动迭代器指针定向修改元素"<<endl;for(string s:lis){
        cout << s << " ";}
    cout<<endl;
    cout<<"___________________"<<endl;// 查
    cout<<"迭代器指针查看"<<endl;
    cout << *iter << endl;for(string s:lis){
        cout << s << " ";}
    cout<<endl;
    cout<<"___________________"<<endl;// 迭代器遍历
    cout << lis.size() << endl;// 清空
    lis.clear();
    cout << lis.size() << endl;return 0;
}

2.3.4 deque队列

deque几乎支持所有vector的API。性能位于vector与list二者之间。最擅长两端存取的顺序容器。

#include <iostream>
#include <deque> // 头文件using namespace std;int main()
{
//    deque<int> deq(5);
//    cout << deq.size() << endl;
//    for(int i = 0; i < deq.size(); ++i)
//    {
//        cout << deq[i] << endl;
//    }
    deque<int> deq = {1,2,3};// 增
    deq.push_back(222);
    cout << deq.size() << endl;// 插入操作// begin()可以返回指向第一个元素的迭代器指针,+2是在第三个位置上插入333
    deq.insert(deq.begin()+2,333);// 删// 删除最后一个元素
    deq.pop_back();// 删除了第二个元素
    deq.erase(deq.begin()+1);// 删除了倒数第二个元素
    deq.erase(deq.end()-2);// 改
    deq[1] = 666;
    deq.at(0) = 888;// 查
    cout << deq[1] << endl;for(int i :deq){
        cout << i <<" ";}
    cout << endl;
    cout << "---------------------" <<  endl;for(int i = 0; i < deq.size(); ++i){
        cout << deq[i] << " ";}
    cout <<  endl;// 判断是否为空,0非空,1空
    cout << deq.empty() << endl;// 清空
    deq.clear();
    cout << deq.empty() << endl;// 迭代器遍历先省略return 0;
}

2.4 关联容器

关联容器的各个元素之间没有严格顺序,虽然内部具有排序特点。但是在使用时没有任何顺序相关的接口。底层是由树构成,(红黑树)

最常见的关联容器就是map-键值对映射

对于map而言,键具有唯一性,键通常使用字符串类型,值任何类型。通过键找到对应的值。

关联容器常用函数

#include <map> // 头文件
map<string,int> ma;//创建一个value为int类型的关联容器
map<string,int> ma1 = {{"年龄",99},{"身高",250}};
ma.insert(pair<string,int>("体重",70));   // 插入元
if(ma.find("身高") == ma.end())
    {
        cout << "没有身高元素" << endl;
    }
int re = ma.erase("身高"); // 删除元素,返回值1成功,0失败

#include <iostream>
#include <map> // 头文件using namespace std;int main()
{// map创建初始化 C++11支持
    map<string,int> ma1 = {{"年龄",99},{"身高",250}};
    cout << ma1.size() << endl; // 2    map<string,int> ma;//创建一个value为int类型的关联容器
    cout << ma.size() << endl;//0:容器大小// 增
    cout << "增操作:"<< endl;
    ma["身高"] = 180;
    ma.insert(pair<string,int>("体重",70));   // 插入元素
    cout <<"身高"<< ma["身高"] << endl;
    cout <<"体重"<<ma["体重"] << endl;
    cout << "--------------------" << endl;// 改
    cout << "改操作:"<< endl;
    ma["身高"] = 165;
    cout << "身高"<<ma["身高"] << endl;
    cout << "--------------------" << endl;// 查
    cout << "查操作:" << endl;
    cout << "身高"<<ma["身高"] << endl;if(ma.find("身高") == ma.end()){
        cout << "没有身高元素" << endl;}else{
        cout << "身高"<<ma["身高"] << endl;}
    cout << "--------------------" << endl;// 删
    cout << "删除操作前 "<<ma.size() << endl;
    cout << "删除操作:" << endl;int re = ma.erase("身高"); // 删除元素,返回值1成功,0失败
    cout << "身高删除返回值:" << re << endl;if(ma.find("身高") == ma.end()){
        cout << "没有身高元素" << endl;}else{
        cout << "身高"<<ma["身高"] << endl;}
    cout <<"删除操作后 "<< ma.size() << endl;
    cout << "--------------------" << endl;//清空操作
    cout << "清空操作" << endl;
    ma.clear();
    cout << ma.size() << endl;
    cout << "--------------------" << endl;return 0;
}

2.5 迭代器

迭代器是一个特殊的指针,主要用于容器的元素读写以及遍历。

如果迭代器不进行修改,建议使用只读迭代器,const_iterator,反之使用iterator.

#include <array>
#include <vector>
#include <list>
#include <deque>
#include <map> // 头文件// 迭代器遍历 string
for(string::const_iterator iter = s.begin();iter != s.end(); ++iter)
// 迭代器遍历array
for(array<int,5>::const_iterator iter = arr.begin();iter != arr.end();iter++)
// vector 迭代器遍历
for(vector<string>::const_iterator iter = vec.begin();iter != vec.end();iter++)
// 迭代器遍历list
for(list<string>::const_iterator iter = lis.begin();iter != lis.end();++iter)
// 迭代器遍历deque
for(deque<string>::const_iterator iter = de.begin();iter != de.end();iter++)
// 迭代器遍历map
for(map<string,int>::const_iterator iter = ma.begin();iter != ma.end(); iter++)
{
    // first 是键 second 是值
    cout << iter->first << " " << iter->second << endl;
}

#include <iostream>
#include <array>
#include <vector>
#include <list>
#include <deque>
#include <map> // 头文件using namespace std;int main()
{
    string s = "abdcdfg";
    // 迭代器遍历 string
    cout<<"迭代器遍历 string:"<<endl;
    for(string::const_iterator iter = s.begin();
        iter != s.end(); ++iter)
    {
        cout << *iter << " ";
    }
    cout << endl;
    cout << "--------------" << endl;    // 迭代器遍历array
    cout<<"迭代器遍历array:"<<endl;
    array<int,5> arr = {23,2,5,87,2};
    for(array<int,5>::const_iterator iter = arr.begin();
        iter != arr.end();iter++)
    {
         cout << *iter << " ";
    }
    cout << endl;
    cout << "--------------" << endl;    // vector 迭代器遍历
    cout<<"vector 迭代器遍历:"<<endl;
    vector<string> vec(6,"hello");
    for(vector<string>::const_iterator iter = vec.begin();
        iter != vec.end();iter++)
    {
        cout << *iter << " ";
    }    cout << endl;
    cout << "--------------" << endl;    // 迭代器遍历list
    cout<<"迭代器遍历list:"<<endl;
    list<string> lis(6,"world");
    for(list<string>::const_iterator iter = lis.begin();
        iter != lis.end();++iter)
    {
        cout << *iter << " ";
    }    cout << endl;
    cout << "--------------" << endl;    // 迭代器遍历deque
    cout<<"迭代器遍历deque:"<<endl;
    deque<string> de(6,"nihao");
    for(deque<string>::const_iterator iter = de.begin();
        iter != de.end();iter++)
    {
        cout << *iter << " ";
    }    cout << endl;
    cout << "--------------" << endl;    // 迭代器遍历map
    cout<<"迭代器遍历map:"<<endl;
    map<string,int> ma;
    ma["年龄"] = 23;
    ma["身高"] = 180;
    ma["体重"] = 65;    for(map<string,int>::const_iterator iter = ma.begin();
        iter != ma.end(); iter++)
    {
        // first 是键 second 是值
        cout << iter->first << " " << iter->second << endl;
    }    return 0;
}

练习:

写一个函数show_array,要求传入两个参数。

void show_array(T t,int len)

第一个参数为数组默认值,第二个参数为数组的长度。

此函数的功能为创建一个长度为len类型是T,默认值为t的数组并输出。

尽量不用使用c语言的数组。

#include <iostream>#include <vector>
using namespace std;
template<class T>
void show_array(T t,int len)
{
    vector<T> vec(len,t);for(T i:vec){
        cout << i << " ";}}int main()
{show_array("a",5);return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mac M1 Parallels CentOS7.9 Install Parallels Tools

一、挂载parallels-tools安装包 mkdir /media/cdrom/ mount /dev/cdrom /media/cdrom/ mount: /dev/sr0 写保护&#xff0c;将以只读方式挂载二、GCC升级 yum install -y centos-release-scl yum install -y devtoolset-8-gcc*# 切换当前会话中gcc版本为8 scl enable devtool…

李沐机器学习系列4---全连接层到卷积

1 从全连接到卷积 1.1 平移不变性 从概率分布的角度来看卷积的定义, f ( τ ) f(\tau) f(τ)是概率密度&#xff0c; g ( t − τ ) g(t-\tau) g(t−τ)是在这个分布下的均值 ( f ∗ g ) ( t ) ∫ − ∞ ∞ f ( τ ) g ( t − τ ) d τ (f*g)(t)\int_{-\infin}^{\infin}f(\t…

vue-打包

打包的作用 说明&#xff1a;vue脚手架只是开发过程中&#xff0c;协助开发的工具&#xff0c;当真正开发完了>脚手架不参与上线 打包的作用&#xff1a; 1&#xff09;将多个文件压缩合并成一个文件 2&#xff09;语法降级 3&#xff09;less sass ts语法解析 打包后…

大创项目推荐 深度学习卫星遥感图像检测与识别 -opencv python 目标检测

文章目录 0 前言1 课题背景2 实现效果3 Yolov5算法4 数据处理和训练5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **深度学习卫星遥感图像检测与识别 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐…

Linux_CentOS_7.9配置时区及NTPdate同步之简易记录

前言&#xff1a;ntpdate命令来自英文词组”NTPdate“的拼写&#xff0c;其功能是用于设置日期和时间。ntpdate命令能够基于NTP协议设置Linux系统的本地日期和时间&#xff0c;利用NTP服务的时钟过滤器来选择最优方案&#xff0c;大大提高了可靠性和精度&#xff0c;让系统时间…

pandas.DataFrame() 数据自动写入Excel

DataFrame 表格数据格式 &#xff1b; to_excel 写入Excel数据&#xff1b; read_excel 阅读 Excel数据函数 import pandas as pd#df2 pd.DataFrame({neme: [zhangsan, lisi, 3]}) df1 pd.DataFrame({One: [1, 2, 3],name: [zhangsan, lisi, 3]})#One是列明&#xff0c;123是…

【LeetCode】150. 逆波兰表达式求值(ASCII码)

今日学习的文章链接和视频链接 leetcode题目地址&#xff1a;150. 逆波兰表达式求值 代码随想录题解地址&#xff1a;代码随想录 题目简介 即将后缀表达式转换成中缀表达式并计算。 给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 …

Linux系统使用超详细(六)~进程管理

目录 一、认识进程 二、进程号 2.1.进程号概念 2.2.进程号作用 三、进程查看 3.1. ps命令&#xff1a; 3.2. top命令&#xff1a; 3.3. htop命令&#xff1a; 3.4. pstree命令&#xff1a; 3.5. pgrep命令&#xff1a; 四、进程状态 五、进程优先级 六、进程优先…

HarmonyOS 应用开发学习笔记 ets自定义组件及其引用 @Component自定义组件

Component注解的作用是用来构建自定义组件 Component组件官方文档 自定义组件具有以下特点&#xff1a; 可组合&#xff1a;允许开发者组合使用系统组件、及其属性和方法。 可重用&#xff1a;自定义组件可以被其他组件重用&#xff0c;并作为不同的实例在不同的父组件或容器…

rime中州韵小狼毫 help lua Translator 帮助消息翻译器

lua 是 Rime中州韵/小狼毫输入法强大的武器&#xff0c;掌握如何在Rime中州韵/小狼毫中使用lua&#xff0c;你将体验到什么叫 随心所欲。 先看效果 在 rime中州韵 输入效果一览 中的 &#x1f447; help效果 一节中&#xff0c; 我们看到了在Rime中州韵/小狼毫输入法中输入 h…

Dockerfile语法和简单镜像构建

Dockerfile是一个用于定义Docker镜像的文本文件&#xff0c;包含了一系列的指令和参数&#xff0c;用于指示Docker在构建镜像时应该执行哪些操作&#xff0c;例如基于哪个基础镜像、复制哪些文件到镜像中、运行哪些命令等。 Dockerfile文件的内容主要有几个部分组成&#xff0c…

数据结构与算法(九)图链式存储

邻接表 度&#xff1a;无向图的度&#xff1a;顶点与邻接点连接的边就做度。有向图的度&#xff1a;指向顶点的边叫做入度&#xff0c;由顶点指向其他邻接点的边叫做出度 顶点&#xff1a;存储自身顶点信息和指向下一个临界点的指针 邻接点&#xff1a;保存临接点的存储下标…