c++_note_7_模板

8. 模板

函数模板也叫泛型编程,就是建立一个通用函数,函数类型和参数类型不具体指定,而是用虚拟类型代表。这个通用函数就叫做函数模板。
模 板 ( 函 数 模 板 和 类 模 板 ) { 模 板 函 数 模 板 类 − − > 对 象 模板(函数模板和类模板) \begin{cases} 模板函数\\ 模板类-->对象 \end{cases} {>
template <typename T1,typename T2>关键字可以告诉c++编译器开始泛型编程。

#include<iostream>
using namespace std;

template <typename T>
void myswap(T &a, T &b)
{
	T c;
	c = a;
	a = b;
	b = c;
}

int main(int argc, char *argv[])
{
	char a = 'a',b = 'b';
	myswap<char>(a,b);		// <>格式对应于template声明
    //myswap(a,b)  可以自动类型推导
	cout<<a<<" "<<b<<endl;
	
	cin.get();
	return 0;
}
/*
b a
*/

如果涉及指针操作,则这样声明:

template <typename T,typename T2>

void func(T *p, T2 n);

8.1 模板机制原理

gcc -S生成的.s文件中的汇编代码展示了函数调用过程。其实是编译器根据函数调用帮我们生成了函数代码。

具体过程是两次编译过程:

  1. 根据代码简易编译模板
  2. 根据调用,替换参数后再次编译

8.2 模板函数与普通函数的区别

函数模板将严格按照类型匹配,不会自动类型转换;普通函数可以隐式类型转换。

#include<iostream>
using namespace std;

template <typename T>
void myswap(T &a, T &b)
{
	cout<<"template"<<endl;
}

void myswap(int a, char b)
{
	cout<<"normal"<<endl;
}

int main(int argc, char *argv[])
{
	int 	a = 1;
	char 	b = 'a';
	
	myswap(a,b);
	myswap(b,a);
	myswap(a,a);
	
	cin.get();
	return 0;
}

/*
normal
normal
template
*/

注意几点:

  • 函数模板可以被重载;
  • c++编译器优先考虑普通函数;
  • 如果函数可以产生一个更好的匹配,则选择模板;
  • 可以通过<>只选择模板。
#include<iostream>
using namespace std;

template <typename T>
void myswap(T &a)
{
	cout<<"template"<<endl;
}

void myswap(int a)
{
	cout<<"normal"<<endl;
}

int main(int argc, char *argv[])
{
	int a = 1;
	float b = 1.0;
	
	myswap(a);
	myswap(b);

	cin.get();
	return 0;
}


/*
normal
template
template
*/

8.3 类模板

在表示数组、表、图等数据结构时,可以将数据类型和算法分离。

模板类本身是抽象的,使用时要用尖括号<>提供数据类型。

类模板的使用实际上是将类模板实例化成一个具体的模板类。

#include<iostream>
using namespace std;

template <typename T>
class MyClass{
private:
	T a;
public:
	MyClass(T a)
	{
		this->a = a;
	}
	void printA()
	{
		cout<<a<<endl;
	}
};

void useMyClass(MyClass<int> &c)
{
	c.printA();
}

int main(int argc, char *argv[])
{
	MyClass<int> c(1);
	useMyClass(c);
	cin.get();
	return 0;
}

类模板函数有3种实现方法:

  • 在类的内部
  • 类的外部,在同一cpp中
  • 类的外部,在不同头文件和hpp

下面是第二种实现方式。

template <typename T>
class A{
protected:
	T a;
public:
	A(T a)
	{
		this->a = a;
	}
	void printA();
};

template <typename T>
void A<T>::printA()
{
	cout<<a<<endl;
}

因为模板的原理是二次编译,所以涉及友元函数时,要再次注明<T>

#include<iostream>
using namespace std;

template <typename T>
class A {
public:
	T a;
public:
	A(T a)
	{
		this->a = a;
	}
	friend ostream& operator<< <T>(ostream &out, A &c);
};

template <typename T>
ostream & operator<< (ostream &out, A<T> &c)
{
	out << c.a << endl;
	return out;
}

切记,友元函数在这里这能重载重定向操作符`<<,>>!!!!!

第三种方式:

//头文件
#pragma once
template <typename T>
class A {
public:
	T a;
public:
	A(T a)
	{
		this->a = a;
	}
	friend ostream& operator<< <T>(ostream &out, A &c);

};

//hpp
#include <iostream>
#include "myclass.h"
template <typename T>
ostream & operator<< (ostream &out, A<T> &c)
{
	out << c.a << endl;
	return out;
}

//main
#include<iostream>
using namespace std;
#include "myclass.hpp"

int main(int argc, char *argv[])
{
	A<int> c(1);

	cin.get();
	return 0;
}

8.3.1 类模板派生

继承时要知道父类所占内存大小,所以仍然要用尖括号<>提供数据类型。

template <typename T>
class A{
protected:
	T a;
public:
	A(T a)
	{
		this->a = a;
	}
	void printA()
	{
		cout<<a<<endl;
	}
};

class B : public A<int>
{
private:
	int b;
public:
	B(int a, int b) : A<int>(a)
	{
		this->b = b;
	}
	void printB()
	{
		cout<<b<<endl;
	}
};

如果要从模板类派生模板类,则要这样做:

template <typename T>
class A{
protected:
	T a;
public:
	A(T a)
	{
		this->a = a;
	}
	void printA()
	{
		cout<<a<<endl;
	}
};

template <typename T>
class B : public A<T>
{
private:
	T b;
public:
	B(T a, T b) : A<T>(a)
	{
		this->b = b;
	}
	void printB()
	{
		cout<<b<<endl;
	}
};

8.3.2 类模板中的static关键字

每一种<T>,都拥有自己的一个静态变量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值