C++印刷模板使用方法详解

目录
  • 一、泛型编程
  • 二、模板(初阶)
    • 1.函数模板
      • 1.单参数类型
      • 2.多参数类型
      • 3.模板函数和自定义函数
    • 2.类模板
    • 3.模板不支持分离编译

在了解string之前,我们需要了解模板等等的一些铺垫知识,让我们开始吧!

一、泛型编程

泛型编程是什么意思呢?我们通过下面的例子来具体了解:

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}
void Swap(double& left, double& right)
{
	double temp = left;
	left = right;
	right = temp;
}
int main()
{
	int a = 1, b = 2;
	Swap(a, b);
    double c=1.33,d=2.33;
    Swap(a,b)
}

就拿交换函数来说,当我们交换不同类型的变量的值,那就需要不停的写交换函数的重载,这样代码复用率就较低,那我们能不能创造一个模板呢??

一个Swap的模板,但是我可以用不同的类型去实现这个模板,继而试用它。

如果在 C++ 中,也能够存在这样一个 模具 ,通过给这个模具中 填充不同材料 ( 类型 ) ,来 获得不同材料的铸件 ( 即生成具体类型的代码)。 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

二、模板(初阶)

模板分为:函数模板和类模板

1.函数模板

1.单参数类型

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定 类型版本。 就拿Swap来说: typename 是 用来定义模板参数 关键字,T是类型(也可以用class,(class T))

template<typename T>
void Swap(T& left, T& right)
{
	T temp = left;
	left = right;
	right = temp;
}
int main()
{
	    int a = 1, b = 2;
	    Swap(a, b);
        int x = 1, y = 2;
		Swap(x, y);
		double m = 1.1, n = 2.2;
		Swap(m, n);
		char p = 'a', q = 'b';
		Swap(p, q);
        Swap(m,a);//不同类型
}

那么,具体是怎样实现的呢?

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模 板就是将本来应该我们做的重复的事情交给了编译器。

编译器通过类型推演,将函数模板进行实例化,对应的T就会替换成具体的类型,模板实例化是用几个实例化几个,不是所有不同类型都提前模板实例化。

1.当变量类型相同,但是变量不同,调用Swap();模板实例化只会实例化一个,因为虽然变量不同,但类型相同,模板实例化就是将T换成具体的类型。

2.当Swap(m,a),变量是不同类型时,会发生什么??

因为在推演void Swap(T& left, T& right);时,T的类型不明确,就会发生错误(推演报错),直接报错

但如果不用模板,我们自己这样:

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}
int main()
{
  int a=2;
  double b=2.22;
  Swap(a,b);
}

可能有人就会想:在Swap(a,b)中,会不会a和b发生饮食类型转化呢?较小的类型转化成较大的类型。

当然不会:隐式类型转化只有在 赋值:b=3;(产生临时变量);函数传参的时候(产生临时变量),才会发生隐式类型转化。

函数形参是引用,当类型是引用时,我们就要小心:是否会发生权限放大?当b传值时,中间的临时变量具有常性(只读),而形参是可读可写,权限就会放大,也是不可以通过的,除非加了const,但是加了const就无法交换了,所以这样还是行不通的!

自动推演实例化和显式实例化:

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; //强制类型转化也是产生临时变量,不是改变a1
	cout << Add(a1, (int)d2) << endl;
	// 显示实例化
	cout << Add<double>(a1, d2) << endl;//隐式类型转化
	cout << Add<int>(a1, d2) << endl;
	return 0;
}

在自动推演实例化中,必须强转,不然还是和之前问题一样,该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错。 (推演报错)

不强转情况:显示实例化,:在函数名后的<>中指定模板参数的实际类型(我让你怎么来你就怎么来!)

在函数名后加入了指定模板参数后,就会在实例化时,T直接是指定的类型,这样就会发生隐式类型转换。

注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅 Add(a1, d1); 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化

2.多参数类型

template<typename T1,typename T2),T1,T2为不同类型,当然参数个数大于等于2

template<class t1,class t2>
t1 Add(const t1& left, const t2& right)
{
	return left + right;
}
int main()
{
	int a = 1, b = 2;
	double m = 2.22, n = 3.33;
	cout << Add(a, b) << endl;
	cout << Add(m, n) << endl;
	cout << Add(a, m) << endl;
	cout << Add(n, b) << endl;
}

此时,当Add(不同类型时),就不会发生推演错误,你是什么类型就会推演成什么模板函数。

3.模板函数和自定义函数

当模板函数和自己实现的函数是否可以同时存在时?

//专门处理int的加法函数
int Add(int left, int right)
{
	return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
	return left + right;
}
int main()
{
	int a = 1, b = 2;
	Add(a, b);
	Add<int>(a, b);
	return 0;
}

当自己写的函数和模板函数同时存在时,二者不会冲突,在之前我们讲过他们的函数名修饰规则是不同的。

同时存在,且调用时,首先会调用自己写的函数。因为模板函数相当于一个半成品,他需要推演实例化才会生成具体的函数,所以当然先使用自己实现的。

如果一定要使用模板函数的话,就需要显示实例化:Add<int>(a,b);

这就叫泛型编程,与具体的类型无关!

2.类模板

类模板与函数模板不同的是:类模板统一显式实例化,不需要推演,或者说没有推演的时机,而函数模板实参传递形参时,就会发生推演实例化。

格式:

template<typename T>
class Stack
{
public:
	Stack(int capacity = 4)
	{
		_a = (T*)malloc(sizeof(T)*capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	 ......
private:
	T* _a;
	int _top;
	int _capacity;
};
int main()
{
	// 显示实例化
	Stack<double> st1; // double
	st1.Push(1.1);
	Stack<int> st2; // int
	st2.Push(1);
	// s1,s2是同一个类模板实例化出来的,但是模板参数不同,他们就是不同类型
	return 0;
}

可能有人会问:s1=s2; 会不会发生隐式类型转换呢?当然不会,隐式类型转换只有在类型相近才会发生。

接下来创建一个数组类模板:

namespace mj
{
	template<class T>
	class array
	{
	public:
		inline T& operator[](size_t i)  //这里引用做返回值的目的是除了减少拷贝构造,还有可以修改返回值,直接可以修改数组里的值
		{
			assert(i < N);  //严格控制越界访问的情况
			return _a[i];
		}
	private:
		T _a[N];
	};
}
int main()
{
	mj::array<int> a1;
	for (size_t i = 0; i < N; ++i)
	{
		//相当于: a1.operator[](i)= i;
		a1[i] = i;
	}
	for (size_t i = 0; i < N; ++i)
	{
		// a1.operator[](i)
		cout << a1[i] << " ";
	}
	cout << endl;
	for (size_t i = 0; i < N; ++i)
	{
		a1[i]++;
	}
	for (size_t i = 0; i < N; ++i)
	{
		cout << a1[i] << " ";
	}
	cout << endl;
	return 0;
}

我们可以发现,类对象居然也可以使用数组那一套了??当然是取决于运算符重载。

他与普通数组最大的区别是:

1. 普通数组对于数组越界的这种情况,只能随机的抽查!而我们自己实现的类模板可以严格的控制越界访问这种情况!别说越界修改,越界访问都不行!

2.效率上因为[]是运算符重载,使用就会调用函数开辟栈帧,但是若定义到类中,并且加inline,就对于效率来说,那真是完美!

3.模板不支持分离编译

我们在实现数据结构的时候,是不是会经常去分几个文件去实现不同模块的功能?

(个人习惯).h文件中,我们写声明;.cpp文件中,我们写定义;test.cpp中,我们测试使用

但今天学习的模板就不能这么做了!!具体不能怎么做,我们上代码:

如果这样写的话,他就会报链接错误(就是在使用时找不到定义)

我们知道,在预处理阶段,就会将.h头文件展开,test.cpp中只有声明,在调用函数时,就会去找他的地址(call stack()),那么在编译的时候,编译器允许只有声明没有函数,相当于你可以先给他一个承诺,兑不兑现后面再说。

但在链接的时候,test.cpp中,却不能找到它的地址,这是为什么??这就是模板和其他的区别!

链接错误原因:

.cpp中的定义,不是实例化模板,他只是一个模板,没有任何实例化成任何类型。所以你在使用类模板的时候,压根就找不到它的定义,当然也找不到地址了,这不就链接错误了吗?

看上图:stack<int> st; 显示实例化,但是.h中只有声明,test.cpp用的地方实例化了,但是定义的地方stack.cpp却没有实例化,只是一个模板。

用的地方在实例化,但是有声明,没有定义;

定义的地方没有实例化。

解决方法:

那转来转去就是一个问题:stack.cpp中定义没有实例化!!

办法一:

你没有实例化,我给你补上:在定义后面加一个实例化

template<class T>
Stack<T>::Stack(int capacity = 4)
{
	cout << "Stack(int capacity = )" << capacity << endl;
	_a = (T*)malloc(sizeof(T)*capacity);
	if (_a == nullptr)
	{
		perror("malloc fail");
		exit(-1);
	}
	_top = 0;
	_capacity = capacity;
	template
	class Stack<int>;

但是就会有另一个问题,我如果使用的时候,创建不同类型,那模板实例化就要有不同类型,那就要一直补实例化,总不肯用一个补一个吧。

方法二:

那就是模板的编译不分离:(不要将定义和声明一个到.cpp,一个到.h)

当放在一个文件中时,在编译时,.h 文件展开后,定义和声明都在test.cpp中,那直接就会完成模板实例化,就有了函数地址,不需要再去链接了。

链接:只有声明没有定义才会到处去找定义。

那有人就会问,加inline可以吗?

inline当然不可以,加了inline后,直接不产生符号表,还存在什么地址吗?

直接放类中也不行,当数据量大的时候,都挤到一推,代码阅读性很差,会傻傻搞不清!

到此这篇关于C++印刷模板使用方法详解的文章就介绍到这了,更多相关C++印刷模板内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解C++中函数模板的定义与使用

    目录 1. 前言 2. 初识函数模板 2.1 语法 2.2 实例化 2.3 实参推导 3. 重载函数模板 1. 前言 什么是函数模板? 理解什么是函数模板,须先搞清楚为什么需要函数模板. 如果现在有一个需求,要求编写一个求 2 个数字中最小数字的函数,这 2 个数字可以是 int类型,可以是 float 类型,可以是所有可以进行比较的数据类型…… 常规编写方案:针对不同的数据类型编写不同的函数. #include <iostream> using namespace std; //针对 int

  • C++多态特性之派生与虚函数与模板详细介绍

    目录 继承与派生 虚函数 父类代码如下 模板 函数模板 类模板 字符串 继承与派生 C ++ 是面向对象编程,那么只要面向对象,都会有多态.继承的特性.C++是如何实现继承的呢? 继承(Inheritance)可以理解为一个类从另一个类获取成员变量和成员函数的过程.例如类 B 继承于类 A,那么 B 就拥有 A 的成员变量和成员函数. 在C++中,派生(Derive) 和继承是一个概念,只是站的角度不同.继承是儿子接收父亲的产业,派生是父亲把产业传承给儿子. 被继承的类称为父类或基类,继承的类称

  • C++图文并茂分析讲解模板

    目录 1.内容引入 2.模板函数 C语言写交换函数 C++写交换函数 模板交换函数的语法及其原理 语法 原理 理解显示实例化和隐式实例化 关于编译器也是懒人这件事 3.类模板 1.内容引入 ​ 不知道大家是否在高中时背过英语范文模板,以下就是博主的回忆: ​ 这篇模板是一些英语比较好的老师写的. ​ 每当碰到感谢信时,我都会狂喜,尽管感谢的内容不同,地点不同,我都可以去根据模板,再根据作文分析模板的那些空对应应该填入什么. 其实呢c++中也用模板,但是这个时候,我们是写模板的人,而编译器变成了那

  • C++模板编程特性之移动语义

    目录 C++的值类型 右值引用与移动构造和移动赋值 C++的值类型 我们知道,每个变量都有类型,或整形或字符型等来进行了分类,不仅如此,C++表达式(带有操作数的操作符.字面量.变量名等)在类型的属性上,还有一种属性,即值类别(value category).且每个表达式只属于三种基本值尖别中的一种:左值(lvalue),右值(rvalue),将亡值(xvalue),每个值类别都与某种引用类型对应. 其中,左值和将亡值成为泛左值(generalized value,gvalue),纯右值和将亡值

  • C++泛型模板约束深入讲解

    CPP参考:(新标准) 传送门 模板对于类型的约束: 约束 template_get_size 泛型T只允许接受类型:list<T>,其实为 C/C++ 泛型模板例化特性,但与泛型模板例化略微有些区别,因为是带泛型类型约束条件的特例化. template<typename T> class list { public: int count = 0; }; template<typename T> struct template_get_size; template<

  • C++模板超详细介绍

    目录 1.前言 2.函数模板 3.类模板 1.前言 模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码. 模板是创建泛型类或函数的蓝图或公式. 通常有两种形式:函数模板和类模板: 2.函数模板 函数模板针对仅参数类型不同的函数 一般形式为: template <类型形式参数表> 类型 函数名( 形式参数表) { // 函数的主体 } 函数调用实例: #include <iostream> #include <string> using namespa

  • C++可变参数模板深入深剖

    目录 概念 模板定义 参数包展开 递归函开 逗号表达式展开 emplace 使用方法 工作原理 意义 总结 概念 C++11 新增一员猛将就是可变参数模板,他可以允许可变参数的函数模板和类模板来作为参数,使得参数高度泛化. 在 C++11 之前类模板和函数模板中只能包含固定数量模板参数,而且也有可变参数的概念,比如 printf 函数就能够接收任意多个参数,但这是函数参数的可变参数,并不是模板的可变参数.可变模板参数无疑是一个巨大的改进,但由于可变参数模板比较抽象,因此使用起来并不会太简单. 模

  • C++印刷模板使用方法详解

    目录 一.泛型编程 二.模板(初阶) 1.函数模板 1.单参数类型 2.多参数类型 3.模板函数和自定义函数 2.类模板 3.模板不支持分离编译 在了解string之前,我们需要了解模板等等的一些铺垫知识,让我们开始吧! 一.泛型编程 泛型编程是什么意思呢?我们通过下面的例子来具体了解: void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } void Swap(double&

  • 对Pycharm创建py文件时自定义头部模板的方法详解

    如下所示: # -*- coding: utf-8 -*- """ ------------------------------------------------- File Name: ${NAME} Description : Author : ${USER} date: ${DATE} ------------------------------------------------- Change Activity: ${DATE}: ----------------

  • VUE 自定义组件模板的方法详解

    本文实例讲述了VUE 自定义组件模板的方法.分享给大家供大家参考,具体如下: 先说下需求吧,因为客户的用户群比较大,如果需求变动,频繁更新版本就需要重新开发和重新发布,影响用户的体验,考虑到这一层就想到,页面展示效果做动态可配,需求更新时,重新配置一份模板录入到数据库,然后根据用户选择的模板进行展示. 关于页面展示做的动态可配,我是参考vue的Component组件方式,开始时可能会遇到组件定义后不能加载的情况,并在控制台如下错误:You are using the runtime-only b

  • spring boot使用thymeleaf模板的方法详解

    前言 Thymeleaf 是一个跟 Velocity.FreeMarker 类似的模板引擎,它可以完全替代 JSP .相较与其他的模板引擎,它有如下三个极吸引人的特点: 1.Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果.这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式.浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以

  • PHP 基于Yii框架中使用smarty模板的方法详解

    第一种方法按照YII系统的办法生成视图觉得有点麻烦,觉得用smarty更省事.尝试着把smarty模板加进来了. 复制代码 代码如下: date_default_timezone_set("PRC");class PlaceController extends CController {protected $_smarty;function __construct(){parent::__construct('place');//需要一个参数来调用父类的构造函数,该参数为控制器ID$p

  • intellij idea设置统一JavaDoc模板的方法详解

    idea设置统一JavaDoc模板 设置普通注释 按照如下导航找到位置: Setting > Editor > File and Code Templates > Includes > File Header 复制如下,即可实现简单的JavaDoc注释 /** * @author : ChenLiWu * @date : ${DATE} ${TIME} */ 自定义模板 方法注释 按照如下导航找到位置: Setting > Editor > Live Templates

  • bootstrap轮播模板使用方法详解

    本文实例为大家分享了bootstrap轮播模板展示的具体代码,供大家参考,具体内容如下 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Document</title> <link rel="stylesheet" href="https://cdn.bootcss

  • 对python实现模板生成脚本的方法详解

    最近项目需要,针对主项目提取一个小的基础版本,供于在新建项目时使用,所以就有这个python模板生成脚本,其作用如下: 1.通过配置文件来控制模板中的数据.格式化的过滤条件 2.执行后会把目录下所有的文件都会执行一篇 #!/usr/bin/python #encoding: utf-8 import json import codecs import os def get_files(root_path): for dir in os.walk(root_path): if dir[2]: fo

  • c++中的单例类模板的实现方法详解

     1.什么是单例模式 在架构设计时,某些类在整个系统生命周期中最多只能有一个对象存在 ( Single Instance ).如超市收银系统,其外观主要由显示器(1个).扫描枪(1个).收款箱(1个)组成,在系统正常运行期间这三部分都是唯一存在的:也就是说,显示器.扫描枪.收款箱这三部分都应该有各自的类,并且每个类只能唯一地实例化一个对象,基于这种模式的程序设计,称为单例模式. !!!单例模式只能创建一个对象,且该对象的生命周期伴随系统的整个运行期间. 2.怎么实现单例模式 思考:如何定义一个类

  • IDEA2020.3.2版本自动注释类和方法注释模板配置步骤详解

    想来大家在学习或者开发的环境下经常要自己注解类和方法吧:下面我来为大家讲解怎么创建自己的注解模板: 一.配置类自动注解 1.打开idea file–>Settings–> 2.按照下图的步骤:直接搜索file and code–>导航栏点击File and Code Templates–>点击files文件类型–>然后选择Class文件模板在里面编辑自己想要的模板–>点击apply应用一下–>点击ok.如果还要设置方法模板的话先别急着点ok. 类注解代码片:根据自

随机推荐