C++初阶---array forward_list 模板进阶

news/2024/7/5 7:25:59

array forward_list 模板进阶

  • 1)arrary
  • 2)forward_list
  • 3)模板的特化
    • 1.函数模板特化
    • 2.类模板特化
      • ①全特化
      • ②偏特化
  • 4)模板分离编译
    • ①分离编译
    • ②函数模板分离编译
    • ③类模板其他问题
  • 5)模板总结

1)arrary

静态数组
注意:

  1. 浮点数类对象以及字符串不允许作为非类型模板参数的
  2. 非类型的模板参数必须在编译期就能确认结果

在32位Linux下栈很小只有8MB
不建议使用:如果数据量过大会造成栈溢出
对比vevtor:vector只有12byte地址在栈上,开辟的空间都在堆上
参考:array


2)forward_list

单向链表
注意forward_list无尾插尾删
参考:forwa_list


3)模板的特化

概念:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化

1.函数模板特化

模板的匹配原则:(引入)
例:

template<class T>
bool IsEqual(const T& left, const T& right)
{
	return left == right;
}
bool IsEqual(const char* const & left,const char* const & right)
//bool IsEqual(const char* left, const char* right)
// 函数模板的特化 (针对某些类型的特殊化处理)
{
	return strcmp(left, right) == 0;
}
int main()
{
	cout << IsEqual(1, 2) << endl;
	char p1[] = "dfddf";
	char p2[] = "dfsdf";
	cout << IsEqual<const char*>(p1, p2)<< endl;;
	return 0;
}

注意

  1. (const char* const & left,const char* const & right) ‘*’前面的const修饰的是*left,’*'后面的const才是修饰的left本身,而传入的形参p1,p2是数组名具有const属性 , 写为这样不能识别(const char*& left, const char*& right)
  2. 不能在bool IsEqual(const T& left, const T& right)内用if(T==const char*) return strcmp(left, right) == 0;
    不支持T==const char*此语法

函数模板特化

template<>
bool IsEqual<const char* const>(const char* const &left, const char* const &right)
{
	return strcmp(left, right) == 0;
}

注意

  1. 函数模板只能全特化不能偏特化
  2. 函数模版的全特化版本不参与函数重载解析,并且优先级低于函数基础模版参与匹配
  3. 一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出
  4. 出现下面的报错 因为本身存在类型转换,再引用const和&就会更复杂(不同编译器识别不同)改为 bool IsEqual<char*>(char* left,char* right)或直接重载即可(传字符串会自动推导为char[字符个数]所以不会选中特化)
    在这里插入图片描述

参考:Why Not Specialize Function Templates(为什么不特化函数模板?)

2.类模板特化

①全特化

template<class T1, class T2>
class Data
{
public:
	Data() {cout<<"Data<T1, T2>" <<endl;}
private:
	T1 _d1;
	T2 _d2;
};
template<>
class Data<int, char>
{
public:
	Data() {cout<<"Data<int, char>" <<endl;}
private:
	int _d1;
	char _d2;
};

Data<int, int> d1;
Data<int, char> d2;

②偏特化

  1. 部分特化
// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
	Data() {cout<<"Data<T1, int>" <<endl;}
private:
	T1 _d1;
	int _d2;
};
  1. 参数进一步限制
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
	Data() {cout<<"Data<T1*, T2*>" <<endl;}
private:
	T1 _d1;
	T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
	: _d1(d1)
	, _d2(d2)
	{
		cout<<"Data<T1&, T2&>" <<endl;
	}
private:
	const T1 & _d1;
	const T2 & _d2;
};
Data<int *, int*> d3; // 调用特化的指针版本
Data<int&, int&> d4(1, 2); // 调用特化的指针版本

4)模板分离编译

①分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式

②函数模板分离编译

以下场景,模板的声明与定义分离开

  1. Func.h
#pragma once
#include <iostream>
using namespace std;
template<class T>
void F(const T& x);
void Print();
  1. Func.cpp
#include "func.h"
template<class T>
void F(const T& x)
{
	cout << "void F(const T& x)" << endl;
}
void Print()
{
	cout << "void Print()" << endl;
}
  1. Test.cpp
#include "Func.h"
int main()
{
	F(1);
	Print();
	return 0;
}

编译器报错:
在这里插入图片描述

分析
运行需要预处理编译汇编链接
预处理:完成头文件包含,#define定义的符号和宏的替换,去除注释


每个文件分开预处理
Func.cpp生成Func.i

template<class T>
void F(const T& x);
void Print();
template<class T>
void F(const T& x)
{
	cout << "void F(const T& x)" << endl;
}
void Print()
{
	cout << "void Print()" << endl;
}

在Func.cpp中编译器没有看到对F模板函数的实例化因此不会生成具体的F函数

Test.cpp生成Test.i

template<class T>
void F(const T& x);
void Print();

int main()
{
	F(1);
	Print();
	return 0;
}

编译 对程序按照语言特性进行词法、语法、语义分析,错误检查无误后生成汇编代码注意头文件不参与编译编译器对工程中的多个源文件是分离开单独编译的

汇编 生成汇编语言
两个调用函数F(1) Print()生成汇编代码
call _Z1Fi(待填入的地址)
call z5Print(待添入的地址)

链接:将多个obj(目标)文件合并成一个,并处理没有解决的地址问题
只有在链接的时候才会进行合并

Test.obj中调用的F<int>Print(),但函数F没有实例化没有生成具体的代码因此链接时报错


解决方法1显式实例化

Test.cpp中添加下面的注释代码

#include "func.h"
template<class T>
void F(const T& x)
{
	cout << "void F(const T& x)" << endl;
}
void Print()
{
	cout << "void Print()" << endl;
}
//template
//void F(const int& x);

缺点 用一个类型时,就需要再次显示实例化,麻烦


解决方法2 不分离编译声明和定义或者直接定义在.h中

template<class T>
void F(const T& x);
template<class T>
void F(const T& x)
{
	cout << "void F(const T& x)" << endl;
}

void Print();

注意 源文件为.h.hpp都可以
参考:为什么C++编译器不能支持对模板的分离式编译


③类模板其他问题

类模板分离编译和函数模板同理
.h源文件

template<class T>
class Stack
{
public:
	Stack();
	~Stack();
	void Push(const T& x);
private:
	T* _a //这里没有分号,编译器会报错 因为会对类框架进行检测
	int _top;
	int _capacity;
};
template<class T>
Stack<T>::Stack()
{
	_a = new T[10];
	_top = 0;
	_capacity = 10;
}
template<class T>
Stack<T>::~Stack()
{
	delete[] _a;
	_a = nullptr;
}
template<class T>
void Stack<T>::Push(const T& x)
{
	_a[_top] = x  //这里没有分号,没有实例化所以不会报错
	++_top;
}
  1. 编译器会报错 因为会对类框架进行检测
  2. Push有语法问题,没有被检查出来,编译不报错,模板如果没有实例化,编译器不会去检查模板内部语法错误,我们实例化了栈这个类,对类模板是按需实例化,调用了哪个成员函数就实例化谁

5)模板总结

优点

  1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
  2. 增强了代码的灵活性

缺陷

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

http://www.niftyadmin.cn/n/4608929.html

相关文章

Expo大作战(二十三)--expo中expo kit 高级属性(没干货)

简要&#xff1a;本系列文章讲会对expo进行全面的介绍&#xff0c;本人从2017年6月份接触expo以来&#xff0c;对expo的研究断断续续&#xff0c;一路走来将近10个月&#xff0c;废话不多说&#xff0c;接下来你看到内容&#xff0c;讲全部来与官网 我猜去全部机翻个人修改补充…

bzoj1036: [ZJOI2008]树的统计Count link-cut-tree版

题目传送门 这 算是link-cut-tree裸题啊 不过以前好像没有写过单点修改.............. #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const int M50007; int read(){int ans0,f1,cgetchar();while(c&…

【吴恩达】prompt engineering(原则 迭代 文本概括 推断、订餐机器人)

简介 Introduction 基础的LLM训练的模型&#xff0c;问法国的首都什么&#xff0c;可能会将答案预测为“法国最大的城市是什么&#xff0c;法国的人口是多少”许多 LLMs 的研究和实践的动力正在指令调整的 LLMs 上。指令调整的 LLMs 已经被训练来遵循指令。因此&#xff0c;如…

Linux----进程控制(上)

Linux----进程控制&#xff08;上&#xff09;1&#xff09;进程创建fork()① fork()返回值为什么有两个&#xff08;返回两次&#xff09;&#xff1f;② fork()常见使用场景③ fork()调用失败的原因2&#xff09;进程终止进程退出的情况分类进程退出方法①exit()②_exit()在操…

14.Nginx防盗链Nginx访问控制Nginx解析php相关配置Nginx代理

[toc] 一、Nginx防盗链&#xff1a; 1. 打开配置文件&#xff1a; 增加如下配置文件&#xff1a; [rootxavi ~]# cd /usr/local/nginx/conf/vhost/ [rootxavi vhost]# vim test.com.conf} # location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$# {# expires 7d;# …

安装戴尔OMSA

设置变量versionumcat /etc/redhat-release | awk {print $3} | awk -F . {print $1}versionnamecat /etc/redhat-releaseif [ $versionum 5 ];then echo $versionname" Tikanga" > /etc/redhat-release echo "OM-SrvAdmin-Dell-Web-LX-7.4.0-866.RHE…

Linux----进程控制(下)

Linux----进程控制&#xff08;下&#xff09;3&#xff09;进程等待wait()waitpid()①status②option(阻塞与非阻塞)4&#xff09;进程程序替换替换函数①execl系列&#xff08;l&#xff1a;list&#xff09;②execv系列&#xff08;v&#xff1a;vector&#xff09;场景3&am…

安卓应用安全指南 4.2.2 创建/使用广播接收器 规则书

4.2.2 创建/使用广播接收器 规则书 原书&#xff1a;Android Application Secure Design/Secure Coding Guidebook 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 遵循下列规则来发送或接受广播。 4.2.2.1 仅在应用中使用的广播接收器必须设置为私有&#xff08;必需…