C++模板编程入门

前言


c++文章连载:
1.C++基础
  1.C++基础
  2.C++新增和有变化的关键字
  3.C++的内存管理
2.面向对象
  1.C++的封装和访问权限
  2.C++继承和多态特性
  3.C++的运算符重载
  4.C++静态类和静态成员
  5.C++的友元函数和友元类
3.模板编程和STL
  1.C++模板编程入门
  2.STL的容器类和迭代器
  3.STL的泛型算法
  4.模板特化与类型萃取
  5.STL的其他容器讲解
  6.智能指针与STL查漏补缺
4.杂项
  1.c++各种流操作
  2.依赖,关联,聚合,组合,继承
  3.一些技巧性的代码设计
  
  


1.概述

1、泛型在C++、java、C#等多种语言中都有
2、模板是一种编译时的多态
(1)函数模板定义时typename和class效果是一样的,视个人习惯而使用

2.函数模板和类模板

(1)类名前要加template<typename T>,函数定义前也要加,而且函数的作用域符前面需要加<T>template<typename T> people<T>::people(int a)
使用类库的人需要指定模板的类型,所以使用需要带上类型:People p1(4);
(2)单模板参数的类模板使用,代码实践

3.模板友元函数

3.1、友元函数参数中不带模板的情况
(1)友元函数声明在class内,定义实现写在class外
(2)友元函数参数中类的直接给出具体类型,譬如
(3)这种友元函数实际是削弱了模板参数在使用
3.2、友元函数参数中带模板参数方法1
(1)友元函数声明和定义都写在class内部
(2)虽然写在class内,但仍然是友元,而不是member function
1.因为不能通过对象.友元函数的方式来执行友元函数,所以说明友元函数不是对象的成员。
2.友元函数不能通过this->成员来调用类中的成员,只有通过传参,比如传一个对象形参

friend void print(const people& pn);

发现以下代码效果一样:

friend void print(const person<T>& pn)
	{
		cout << pn.age << endl;
	}
friend void print(const person& pn)
	{
		cout << pn.age << endl;
	}

(3)友元可以适配类的各种模板参数
3.3、友元函数参数中带模板参数方法2
(1)友元函数声明在class内,定义在class外
(2)声明时函数名加后缀,而定义时不用加
(3)需要class和friend function的2个前置声明
(4)调用friend function时可加<实参类型>后缀,也可以不加,但是加就必须加对了

4.模板运算符重载函数

#include <iostream>
#include <string>
using namespace std;

// 定义模板类
template <typename T> class People
{
private:
	T age;

public:
	People(){};
	People(T a):age(a){};
	
	// 运算符重载 +  -		c = a + b;
	People<T> operator+(People<T> &other);
	
	// 运算符重载+=		a += b;  等价于 a = a + b;
	People<T> operator+=(People<T> &other);
	void print(void);
};

template <typename T> People<T> People<T>::operator+(People<T> &other)
{
	People<T> tmp;
	
	tmp.age = this->age + other.age;
	return tmp;
}

template <typename T> People<T> People<T>::operator+=(People<T> &other)
{
//	this->age = this->age + other.age;
	this->age += other.age;
	
	return *this;
}

template <typename T> void People<T>::print(void)
{
	cout << "age = " << this->age << endl;
}

int main(void)
{
	People<string> a("aston");
	People<string> b("zhu");
	People<string> c("");

//	c = a + b;
	a += b;
	a.print();
	/*
	People<int> a(4);
	People<int> b(6);
	People<int> c(0);

	c = a + b;
	c.print();
*/	
	return 0;
}

5.模板友元运算符重载实现

5.1、+作为友元运算符重载
(1)友元函数在class内实现,ok,但是因为是友元实现的,所以参数要有2个
(2)友元函数在class外实现,不ok
(3)友元函数的第三种实现,ok(见代码)
5.2、+=作为友元运算符重载
(1)友元函数在class内实现,ok,但必须带2个参数,带1个不行
(2)友元函数在class外实现,ok
(3)友元函数的第三种实现,ok

#include <iostream>
#include <string>

using namespace std;

//template <typename T> class People;
//template <typename T> People<T>& operator+=(People<T> &a, People<T> &b);

// 定义模板类
template <typename T> class People
{
private:
	T age;

public:
	People(){};
	People(T a):age(a){};
	
	template <typename B> friend People<B>& operator+=(People<B> &a, People<B> &b);
	/*
	friend People<T>& operator+=(People<T> &a, People<T> &b)
	{
		//a.age = a.age + b.age;
		a.age += b.age;
		return a;
	}
	*/
	
//	template <typename U> friend People<U> operator+(People<U> &a, People<U> &b);
	
/*	
	friend People<T> operator+(People<T> &a, People<T> &b)
	{
		People<T> tmp;
		tmp.age = a.age + b.age;
		return tmp;
	}
*/
	void print(void);
};

template <typename T> People<T>& operator+=(People<T> &a, People<T> &b)
{
	a.age += b.age;
	return a;	
}

/*
template <typename T> People<T> operator+(People<T> &a, People<T> &b)
{
	People<T> tmp;
	tmp.age = a.age + b.age;
	return tmp;
}
*/

template <typename T> void People<T>::print(void)
{
	cout << "age = " << this->age << endl;
}

int main(void)
{
#if 0
	People<string> a("aston");
	People<string> b("zhu");
	People<string> c("");

//	c = a + b;
	a += b;
	
	a.print();
#endif
	
#if 1
	People<int> a(4);
	People<int> b(6);
	People<int> c(0);

	a += b;
	a.print();
#endif	
	return 0;
}

6.模板类的继承

类模板与模板类概念:模板类是类模板实例化后的一个产物

#include <iostream>
#include <string>

using namespace std;


// 定义模板类,作为父类
template <typename U1, typename U2> class People
{
public:
	U1 x1;		// x1是double
	U2 x2;		// x2是int
	
	
	People(){};
	People(U1 a, U2 b):x1(a),x2(b){};
	
};

// Man类里T1对应int,T2对应double
// 继承时,父类就是People<double,int>,因为这一步是认符号,不认顺序
// 然后再去到People<T1, T2>类里去,所以是People<double,int>对应了People<T1, T2>
// 于是,在People类内部,T1对应double,T2对应int,这时候是认顺序,不认符号
template <typename T1, typename T2> class Man:public People<T2, T1>
{
public:
	T1 y1;		// T1 int 
	T2 y2;		// T2 double
	
	Man(){};
	Man(T1 a, T2 b):y1(a),y2(b){};
	// 4个参数,顺序:按照y1 y2 x1 x2
	Man(T1 a1, T2 a2, T1 b1, T2 b2):People<T2,T1>(b1, b2),y1(a1),y2(a2){};

};

int main(void)
{
	Man<int,double> m1(4, 5.5, 6, 7.7);		// y1=a, y2=5.5 	x1=6, x2=7.7
	cout << "x1 = " << m1.x1 << ", x2 = " << m1.x2 << endl;
	cout << "y1 = " << m1.y1 << ", y2 = " << m1.y2 << endl;
	return 0;
}

7.非类型模板参数和模板类型推导

7.1、非类型模版参数

template<typename T, int VAL>
T addValue(const T& x)
{
    return x + VAL;
}
template<typename T, T VAL>
T addValue(const T& x)
{
    return x+VAL;
}

参考 https://blog.csdn.net/lanchunhui/article/details/49634077
浮点型(包括float,double)、类、字符串不可以作为非类型模板参数
7.2、类型推导的隐式类型转换
(1)在决定模板参数类型前,编译器执行隐式类型转换,有时候实际类型和看起来会不同
(2)编译器用值类型实例化函数模板,而不是用相应的引用类型
(3)编译器用指针类型实例化函数模板,而不是相应的数组类型
(4)去除const修饰,绝不会用const类型实例化函数模板,总是用相应的非const类型
(5)对于指针来说,指针和 const 指针是不同的类型。

7.3、模板和库
(1)模板无法单独编译,也就不能通过lib连接静态库的形式隐藏实现
(2)模板通常会把声明和定义写在头文件里,所以c++的模板总是开源的

int a = 5;
int &b = a;
People<b>

int a[5];
void func(a);		// 数组做实参,实际实参不是数组而是指针
People<a>

const int a = 5;
People<a>

const int *p1;
int *p2;

说明typename的一种用法,这种用法里typename不等同于class
https://blog.csdn.net/dick_china/article/details/4522253

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值