learn C++ NO.8——初识模板(函数模板、类模板)

这篇具有很好参考价值的文章主要介绍了learn C++ NO.8——初识模板(函数模板、类模板)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

现在是北京时间2023年6月5号13.31分,距离上一篇博客发布已过一周。期间还零零散散进行了一些期末考试,这也说明了我的大一时光快要结束了。我也想抓着期末的尾巴,好好的复习一下前面的所学内容,争取这周能够更一下简单数据结构的博客。

1.泛型编程

1.1.什么是泛型编程?

泛型编程是一种编程范式,它可以让代码更加通用和灵活。它通过使用类型参数来实现代码的重用,而不是为每种类型编写不同的代码。具体来说,泛型编程允许我们编写可以适用于多种类型的代码,而不必为每种类型都编写一套代码。这样可以提高代码的复用性和可维护性,同时也可以减少代码的冗余和错误。举一个生活中的例子,泛型编程其实是类似于格式化的生产工艺品,只要有一个模具,就可以通过这股模具来大批量生产一样的东西,使生产效率上升。而模板可以复用代码,根据模板参数来适配不同的类型,使得一套通用代码可以适配多种场景。编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

learn C++ NO.8——初识模板(函数模板、类模板)

2.函数模板

2.1.什么是函数模板

函数模板是一种通用的函数定义,可以用来定义多个具有相同基本结构但不同数据类型的函数。函数模板是通过在函数定义中使用一个或者多个类型参数来实现的。类型参数可以用来定义函数参数和返回值的数据类型,以及函数体内使用的变量和常量的数据类型。函数模板可以用于任意的数据类型,从而提高了代码的复用性和可读性。在实际的编程中,函数模板可以用于实现通用算法、容器类和其他的常用数据结构。

2.2.为什么需要函数模板

请看下面的场景,假设需要多个类型的变量进行交换操作,在前面的c语言和c++的代码写法是这样的。

// C
void SwapInt(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

void SwapDou(double* x, double* y)
{
	double tmp = *x;
	*x = *y;
	*y = tmp;
}

// CPP -->(函数重载)

void Swap(int& x,int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

void Swap(double& x,double& y)
{
	double tmp = x;
	x = y;
	y = tmp;
}


可以看到上面的代码的重复率很高,而且只有新需要的类型就要手动添加一份,非常麻烦。其次就是代码不方便维护,有一点错每份代码都要跟着修改,非常的不方便。于是乎我们的c++祖师爷们就整出了函数模板。下面正式介绍函数模板。

2.3.函数模板格式

//两种都可以
template<typename T1,typename T2,...>
//template<class T1,class T2,...>
返回类型 函数名(参数)函数定义

首先,需要写关键字template,后面紧跟一对尖括号。尖括号内部存放的是类型参数。然后换行写函数的定义。下面我以Swap函数来举例。


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

learn C++ NO.8——初识模板(函数模板、类模板)

2.4.函数模板实现原理

模板函数在被调用时,编译器会根据不同的数据类型,生成对应的函数实例。编译器会根据实际传入的参数类型,选择合适的函数实例进行调用。模板函数其实是类似我们能够生成各个颜色手机壳的那个模具,而参数类型类似于手机壳的颜色。当我们需要红色的手机壳,就将材料导入模具中并加上红色颜料,便得到了红色的手机壳。
learn C++ NO.8——初识模板(函数模板、类模板)

通过调试转到反汇编查看汇编可以看到,编译器确实会自动生成对应数据类型的模板函数实例,并调用模板函数实例。

2.5.函数模板的实例化

用各种不同类型的参数去调用函数模板去实例化时,这一行为叫做函数模板的实例化。函数模板实例化分为两种,分别是显示实例化和自动推导类型实例化。下面就通过代码来举例。

  1 #include<iostream>  
  2   
  3 using namespace std;  
  4   
  5 template<typename T>  
  6 T Add(const T& x, const T& y)  
  7 {  
  8   return x + y;  
  9 }                                                                             
 10                                                                               
 11 int main()                                                                    
 12 {                                                                             
 13   int i1 = 10;                                                                
 14   int i2 = 20;                                                                
 15   double d1 = 3.14;                                                           
 16   double d2 = 1.23;                                                           
 17   
 18   //编译器自动推导  
 19   cout << Add(i1,i2) << endl;  
 20   cout << Add(d1,d2) << endl;
 21   cout << Add(i1,(int)d2) << endl;  
 22   cout << Add((double)i1,d2) << endl;  
 23                
 24  //显示实例化
 25  
 26   cout << Add<double>(d1,d2) << endl;
 27   cout << Add<int>(d1,d2) << endl;
 28                                                                                        
 29   return 0;
 30 }          

learn C++ NO.8——初识模板(函数模板、类模板)

对于这里模板的模板参数需要用const修饰呢?这是因为int类型的参数提升成double类型参数会产生一个临时变量。而临时变量具有常属性,这里的const修饰模板参数后,可以避免权限放大的问题而导致编译器报错。 为什么编译器可以自动推导类型还需要显示实例化呢?请看下面的样例演示。

#include<iostream>

using namespace std;


template<typename T>
T* MyAlloc(size_t sz)
{
  return new T[sz];
}

int main()
{
  //必须显示实例化调用函数模板
  int* pi = MyAlloc<int>(10);
  return 0;
}

3.类模板

在没有类模板之前,使用C语言编写一些容器需要重复大量的模块。因为,C语言不支持类模板,这也是C语言没法提供标准容器库。类模板的出现标志着C++进入了一个新的时代,因为它带来的是STL标准库的横空出世。

//C语言
typedef int DataType;
class Stack
{
public:
    Stack(size_t capacity = 3)
    {
        _array = (DataType*)malloc(sizeof(DataType) * capacity);
        if (NULL == _array)
        {
            perror("malloc申请空间失败!!!");
            return;
        }

        _capacity = capacity;
        _size = 0;
    }

    void Push(DataType data)
    {
        // CheckCapacity();
        _array[_size] = data;
        _size++;
    }

    // 其他方法...

    ~Stack()
    {
        if (_array)
        {
            free(_array);
            _array = NULL;
            _capacity = 0;
            _size = 0;
        }
    }

private:
    DataType* _array;
    int _capacity;
    int _size;
};

如果我们在一个模块里既要用栈存int类型的数据,又要用栈存double类型的数据。该怎么办呢?

class StackInt
{
public:
    StackInt(size_t capacity = 3)
    {
        _array = (int*)malloc(sizeof(int) * capacity);
        if (NULL == _array)
        {
            perror("malloc申请空间失败!!!");
            return;
        }

        _capacity = capacity;
        _size = 0;
    }

    void Push(int data)
    {
        // CheckCapacity();
        _array[_size] = data;
        _size++;
    }

    // 其他方法...

    ~StackInt()
    {
        if (_array)
        {
            free(_array);
            _array = NULL;
            _capacity = 0;
            _size = 0;
        }
    }

private:
    int* _array;
    int _capacity;
    int _size;
};

class StackDouble
{
public:
    StackDouble(size_t capacity = 3)
    {
        _array = (double*)malloc(sizeof(double) * capacity);
        if (NULL == _array)
        {
            perror("malloc申请空间失败!!!");
            return;
        }

        _capacity = capacity;
        _size = 0;
    }

    void Push(double data)
    {
        // CheckCapacity();
        _array[_size] = data;
        _size++;
    }

    // 其他方法...

    ~StackDouble()
    {
        if (_array)
        {
            free(_array);
            _array = NULL;
            _capacity = 0;
            _size = 0;
        }
    }

private:
    double* _array;
    int _capacity;
    int _size;
};

int main()
{
	StackInt SI;//存int类型
	StackDouble SD;//存double类型
	return 0;
}

这样实在是太麻烦了,不仅代码不容易维护,而且对编程效率有着极大的影响。所以我们的祖师爷们整出了类模板。类模板其实就是让编译器可以根据我们的需求实例化出各种类型的类,大大提高了我们的效率。

3.1.类模板定义格式

3.1.1.类模板语法

template<class T,class T2,...>
class classname
{
	//类成员
};

注意:普通类的类名就是类的类型。模板类的的类名就是类名本身,而模板类的类型是类名<模板参数>

3.1.2.模板类的定义

// 类模板
template<class T>
class Stack
{
public:
	Stack(size_t capacity = 3);

	void Push(const T& data);

	// 其他方法...

	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}

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


template<class T>
Stack<T>::Stack(size_t capacity)
{
	/*_array = (T*)malloc(sizeof(T) * capacity);
	if (NULL == _array)
	{
		perror("malloc申请空间失败!!!");
		return;
	}*/
	_array = new T[capacity];

	_capacity = capacity;
	_size = 0;
}

template<class T>
void Stack<T>::Push(const T& data)
{
	// CheckCapacity();
	_array[_size] = data;
	_size++;
}

模板类的成员函数声明定义分离时,需要在每个成员函数声明函数模板。并制定对应的类模板的类型。

3.2.模板类的实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。文章来源地址https://www.toymoban.com/news/detail-471967.html

Stack<int> S1;
Stack<double> S2;

到了这里,关于learn C++ NO.8——初识模板(函数模板、类模板)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】模板初识

    在实际编程中,经常会用到交换函数。比如有整型值的交换,浮点型值的交换,字符型值的交换… 像下面代码这样: 但是,难道对于每一种类型不同的值的交换,我们都要重新写一个对应的交换函数吗?这样的话,代码的复用率和可维护性都会降低。 可以发现,上面的交换

    2023年04月26日
    浏览(28)
  • C++初识模板

    上面这段代码,是一个交换函数的重载,这虽然可以处理不同的类型,但是每个类型都要我们写对应的重载函数,而且复用率不是很高。 祖师爷可能也感觉这样不好用,设计了 模板 这个概念,就类似于印刷术,有一个模子在这里,想怎么印刷就怎么印刷。 泛型编程是一种编

    2024年02月13日
    浏览(32)
  • 【C++初阶】内存管理 && 初识模板

    C/C++的内存分布主要分为 栈区、堆区、数据段和代码段,还有内存映射段。 栈又叫堆栈–非静态局部变量/函数参数/返回值等等,栈是向下增长的。 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口创建共享共享内存,做进程间通信。

    2024年02月04日
    浏览(46)
  • learn C++ NO.9——string(2)

    现在是北京时间的2023年6月15日早上的10点14分。时间过得飞快,现在已经大一的最后一个星期了。明天也是大一最后一次课,线下的实训课。线下实训内容为c语言二级的内容,对我来说跟学校的课效率太低下了,我还是比较喜欢按自己的节奏来,一般我是直接带笔记本过去按

    2024年02月09日
    浏览(35)
  • learn C++ NO.10——string(3)

    现在是北京时间2023年6月22日的早上8点。又是一年端午,时光如梭。这一年来发生的变化太多了,遥想去年此时,我还沉浸在被大学录取的喜悦中,转眼间大一就过去了。这里我也衷心的祝愿您和您的家人端午安康! 上回介绍了下标访问操作符的运算符重载,这里我就不再介

    2024年02月11日
    浏览(33)
  • Learning C++ No.31 【线程库实战】

    北京时间:2023/6/11/14:40,实训课中,实训场地有空调,除了凳子坐着不舒服之外,其它条件都挺好,主要是我带上了我自己的小键盘,并且教室可以充电,哈哈哈,巴士!老师还是非常善解人意滴,并没有强迫我们听她讲C语言二级相关知识,虽然这种实训本质就是在刷题式教

    2024年02月08日
    浏览(39)
  • Learning C++ No.26 【深入学习位图】

    北京时间:2023/5/30/15:30,刚睡醒,两点的闹钟,硬是睡到了2点40,那种睡不醒的感觉,真的很难受,但是没办法,欠的课越来越多,压的我喘不过气了都,早上把有关unordered_set和unordered_map的内容给写完了,所以哈希表有关代码方面的知识,我们就搞定的差不多了,并且现在

    2024年02月07日
    浏览(38)
  • 【高级程序设计语言C++】初识模板

    概念: 函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。 具体格式: templatetypename T1, typename T2,…,typename Tn 返回值类型 函数名(参数列表){} 输出结果: typename是用来定义模板参数,也可以使用class(切记

    2024年02月15日
    浏览(46)
  • learn C++ NO.4 ——类和对象(2)

    在 C++ 中,如果没有显式定义类的构造函数、析构函数、拷贝构造函数和赋值运算符重载函数,编译器会自动生成这些函数,这些函数被称为默认成员函数。 初步了解了默认成员函数,上面的空类Date,其实在程序运行时,编译器会默认生成它的默认成员函数。 小结 当没有显

    2024年02月05日
    浏览(33)
  • learn C++ NO.6——类和对象(4)

    1.1.构造函数体赋值 在创建类的对象时,编译器回去调用类的构造函数,来各个成员变量一个合适的值。 虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。

    2024年02月06日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包