首页 > 编程语言 >【C++】模版初阶

【C++】模版初阶

时间:2024-07-23 22:00:12浏览次数:16  
标签:初阶 函数 int 模版 C++ 实例 Stack 模板

模版

一.泛型编程

当我们要交换两个变量时,可以使用函数重载,如下:

void Swap(int& x, int& y)
{}
void Swap(double& x, double& y)
{}
void Swap(char& x, char& y)
{}

但是函数重载也有不好的地方:

  1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增
    加对应的函数。

  2. 代码的可维护性比较低,一个出错可能所有的重载均出错。

那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?于是有了泛型编程。

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础

在这里插入图片描述

二.函数模版

1.函数模版的概念

  函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

2.函数模板的格式

template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}

template<typename T>
void Swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)

3.函数模版的原理

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

在这里插入图片描述
  在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码。

4.函数模版的实例化

  用不同类型的参数使用函数模板时生成对应的函数,称为函数模板的实例化。模板参数实例化分为:隐式实例化显式实例化

  1. 隐式实例化:让编译器根据实参推演模板参数的实际类型。
  2. 显式实例化:在函数名后的<>中指定模板参数的实际类型。
template<class T>
T Add(const T& x, const T& y)
{
	return x + y;
}

int main()
{
	int a1 = 10, a2 = 20;
	double d1 = 1.1, d2 = 1.2;

	//隐式实例化
	cout << Add(a1, a2) << endl;
	cout << Add(d1, d2) << endl;

	//Add(a1, d2);编译器无法确定此处到底该将T确定为int 或者 double类型而报错

	//推导实例化
	cout << Add(a1, (int)d1) << endl;    //11
	cout << Add((double)a1, d1) << endl; //11.1

	//显示实例化
	cout << Add<int>(a1, d1) << endl;    //11
	cout << Add<double>(a1, d1) << endl; //11.1

	return 0;
}

必须显示实例化的案例:

template<class T>
T* func(int n)
{
	return new T[n];
}
int main()
{
	//int* p = func(10);//无法为 "T" 推导模版参数,编译报错
	int* p = func<int>(10);//必须显示实例化

	return 0;
}

5.模板参数的匹配原则

  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这
    个非模板函数。
  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而
    不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模
    板。
  3. 模板函数不允许自动类型转换(编译报错),但普通函数可以进行自动类型转换。
template<class T>//模版参数
T Add(const T& x, const T& y)//函数模版
{
	return x + y;
}

int Add(const int& x, const int& y)
{
	return (x + y) * 2;
}

int main()
{
	int a = 10;
	int b = 20;
	double d = 1.1;
	cout << Add(a, b) << endl;      //调用现成的Add函数——>60
	cout << Add<int>(a, b) << endl; //调用模版——>30

	cout << Add(a, d) << endl; //调用现成的Add函数,发生隐式类型转换——>22
	cout << Add(a, d) << endl; //若没有现成的Add函数——>编译错误

	return 0;
}

三.类模版

1.类模板的定义格式

template<class T1, class T2, ..., class Tn> 
class 类模板名
{
	// 类内成员定义
};

  模版参数只能给当前类模版使用,使用一次,不能多次使用,同理模版参数也不能多次给函数模版使用,比如:

template<class T>
class Stack
{
public:
	Stack(int n = 4)
		:_array(new T[n])
		,_size(0)
		,_capacity(n)
	{
		cout << "Stack(int n = 4)" << endl;
	}

	void Push(const T& x);
	//void Push(const T& x)
	//{
	//	if (_size == _capacity)
	//	{
	//		//C++需要手动扩容
	//		T* tmp = new T[_capacity * 2];
	//		memmove(tmp, _array, sizeof(T) * _size);
	//		delete[] _array;
	//		_array = tmp;
	//		_capacity *= 2;
	//	}
	//	_array[_size++] = x;
	//}

	~Stack()
	{
		delete[] _array;
		_array = nullptr;
		_size = _capacity = 0;
		cout << "~Stack()" << endl;
	}

private:
	T* _array;
	size_t _size;
	size_t _capacity;
};

//模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误
template<class T>//声明模版参数
void Stack<T>::Push(const T& x)//指定作用域
{
	void Push(const T& x)
	{
		if (_size == _capacity)
		{
			//C++需要手动扩容
			T* tmp = new T[_capacity * 2];
			memmove(tmp, _array, sizeof(T) * _size);
			delete[] _array;
			
			_array = tmp;
			_capacity *= 2;
		}
		_array[_size++] = x;
	}
}

int main()
{
	//类模版:都是显示实例化,实例化出不同的类型

	Stack<int> st1;    //int
	st1.Push(1);
	st1.Push(2);
	st1.Push(3);

	Stack<double> st2; //double
	st2.Push(1.1);
	st2.Push(2.2);
	st2.Push(3.3);

	Stack<int>* pst = new Stack<int>;
	delete pst;

	return 0;
}

在这里插入图片描述

2.类模板的实例化

  类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类

//Stack是类名,Stack<int>才是类型
Stack<int> st1;    //int
Stack<double> st2; //double

//Stack<int>与Stack<double>是不同的类型

标签:初阶,函数,int,模版,C++,实例,Stack,模板
From: https://blog.csdn.net/2203_76003626/article/details/140590190

相关文章

  • C++之迭代器
    1.什么是迭代器?2.如何使用迭代器3.C++迭代器说明4.迭代器的高级应用4.1.Enumerator.hpp4.2.Iterator.cpp4.3.输出结果4.4.更多详细代码1.什么是迭代器?迭代器(Iterator)是按照一定的顺序对一个或多个容器中的元素从前往遍历的一种机制,比如for循环就是一种最......
  • C++学习之路——第二天(指针和引用)
    指针和二维数组概述:不管是几维数组,当你使用数组名a而没有取地址操作符&时,它(a)都会退化为指向其第一层的指针。(一维数组指向首元素的指针,二维数组是第一行数组的指针,三维数组是指向其第一层(也就是第一个二维数组)的指针)1、错误示范inta[3][4]={{1,2,3,4},{5,6,7,8},{9......
  • C++_模板(初阶)
    C++_模板(初阶)泛型编程如何实现一个通用的交换函数呢?voidSwap(int&left,int&right){inttemp=left;left=right;right=temp;}voidSwap(double&left,double&right){doubletemp=left;left=right;right=temp;}v......
  • 【Qt项目制作普通计算器】C++语言
    目录一、概述二、界面设计三、程序代码1、程序代码存放位置2、widget.h文件3、widget.cpp4、main.cpp5、.pro文件6、资源文件的添加一、概述1.规划与设计功能确定:决定计算器将支持哪些基本运算(加、减、乘、除、百分之、平方、开根号、变分数等)。界面设计:设计用......
  • C++核心编程-4、类和对象4—多态
    4.7多态4.7.1多态的基本语法 示例代码如下:#include<iostream>usingnamespacestd;//多态的基本概念//满足动态多态的条件:1、有继承的关系2、子类要重写父类的虚函数//重写:函数返回值类型函数名参数列表完全相同//动态多态的使用://父类的指针或者引用执行......
  • C++3算法比较第一期
    目录1.递推(Iteration)2.递归(Recursion)3.动态规划(DynamicProgramming,DP)递推、递归与动态规划的区别在C++编程中,递推、递归和动态规划是三种重要的算法思想,它们在解决复杂问题时各有特色。下面将分别介绍这三种算法思想,并探讨它们之间的区别。1.递推(Iteration)定义......
  • C++学习笔记(01)——使用VS Code进行C++函数分文件编写
    首先需要下载安装:C/C++ProjectGenerator扩展,就是下图这玩意:下载安装完成后,按ctrl+shift+p打开命令面板,输入createC++project,按回车后可以选择保存工程的文件夹创建好会后生成几个目录:.vscode:里面放一些配置文件之类的,如launch.json、setting.json、tasks.jsoninclude:存......
  • C++题目:DNA排序 代码
    题目描述现在有一些长度相等的 ......
  • 2024年华为OD机试真题-执行时长-C++-OD统一考试(C卷D卷)
    2024年OD统一考试(D卷)完整题库:华为OD机试2024年最新题库(Python、JAVA、C++合集) 题目描述:为了充分发挥GPU算力,需要尽可能多的将任务交给GPU执行,现在有一个任务数组,数组元素表示在这1秒内新增的任务个数且每秒都有新增任务,假设GPU最多一次执行n个任务,一次执行耗时1秒,在保证GPU......
  • [转]从SQLite到Redis:探索C++与多种数据库的交互之道
    转自:【C++风云录】从SQLite到Redis:探索C++与多种数据库的交互之道开启数据库之旅:通过C++与各种数据库交互,事半功倍1.SQLite1.1简介SQLite是一个嵌入式关系型数据库管理系统,提供了一个轻量级的C++接口。它是一个开源的软件库,无需配置服务器或安装管理工具,可以直接在程序中使......