【本文谢绝转载】




【泛型编程】

函数模板

为什么会有函数模板

现象:

函数的业务逻辑一样

函数的参数类型不一样

【最常用】函数模板  显式的调用

【不常用】类型推导

多个参数,参数定义了必须要用

函数模板,实现int类型数组,char字符串排序:

函数模板 与 普通函数的本质区别

函数模板 和 普通函数在一起 的调用型研究:

C++是如何支持函数模板机制的?

函数模板机制结论



类模板

类模板的定义

类模板做函数的参数

类模板的派生成普通类

模板类的派生成模板类

复数类,所有函数都写在类的内部,运算符重载热身

复数类,所有函数都写在类的内部, 类模板

【演示】滥用友元函数的后果--正常的代码准备

复数类,所有函数都写在类的内部(一个CPP中):问题抛出

当模板函数遇到友元函数,问题解决:

滥用友元函数的后果

【结论】:不需要友元函数,不要用友元函数

【结论】模板类的cpp文件 与 .h头文件分开写的时候,要把cpp文件也包含进来

当类模板中有static成员变量时;从类模板的实质分析,编译器你会自动为我们写成两个类

类模板 数组案例

类模板 结构体案例

作业:

-------------------------------------------------------------------------

【为什么会有函数模板】


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
using namespace std;
void swap1(int &a,int &b)
{
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
}

void swap2(char &a,char &b)
{
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
}

int main()
{
	int a = 10;
	int b = 20;
	swap1(a,b);
	cout <


现象:

函数的业务逻辑一样

函数的参数类型不一样


【最常用】函数模板  显式的调用


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
using namespace std;

//template  //告诉编译器,我要开始泛型编程了
template 
void myswap(T &a,T &b)
{
	a = a  ^ b;
	b = a  ^ b;
	a = a  ^ b;
}

int main()
{
	
	int a = 10;
	int b = 20;
	cout <(a,b);
	cout <(c,d);
	cout <


【不常用】类型推导


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
using namespace std;

//template  //告诉编译器,我要开始泛型编程了
template 
void myswap(T &a,T &b)
{
	a = a  ^ b;
	b = a  ^ b;
	a = a  ^ b;
}

int main()
{
	
	int a = 10;
	int b = 20;
	cout <


多个参数,参数定义了必须要用


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
using namespace std;

//template  //告诉编译器,我要开始泛型编程了
template 
void myswap(T &a,T &b,str s)
{
	a = a  ^ b;
	b = a  ^ b;
	a = a  ^ b;
	cout << s << endl;
}

int main()
{
	
	int a = 10;
	int b = 20;
	cout <



函数模板,实现int类型数组,char字符串排序:


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
#include
#include
using namespace std;
template 
void select_sort(T1 array[],T2 len)
{
	if(array == NULL)
	{
		return ;
	}
	for(int i=0;i array[j])
			{
				min = j ;
			}
		}
		int tmp   = array[i];
		array[i]  = array[min];
		array[min]= tmp;
	}
}

template 
void print_arr(T1 array[],T2 len)
{
        if(array == NULL)
        {
                return ;
        }
        for(int i=0;i(arr,n);
	print_arr(arr,n);
	

	char str[]= "Hello,Linux!";
	int m = strlen(str);
	select_sort(str,m);
	print_arr(str,m);
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp  && ./run 
0 1 2 3 4 5 6 7 8 9 
! , H L e i l l n o u x 
chunli@http://990487026.blog.51cto.com~/c++$



函数模板 与 普通函数的本质区别


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
#include
#include
using namespace std;

//swap 在 C++是个库函数
template 
void my_swap(T &a,T &b)
{
	cout <



函数模板 和 普通函数在一起 的调用型研究:


【补充】在同一作用域,可以发生函数重载


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
/*
	1 函数模板可以像普通函数一样被重载
	2 C++编译器优先考虑普通函数
	3 如果函数模板可以产生一个更好的匹配,那么选择模板
	4 可以通过空模板实参列表的语言限定编译器只通过模板匹配
*/

#include
#include
#include
using namespace std;

template 
T Max(T a,T b)		//不要引用类型
{
	cout <b?a:b;
}
template 
T Max(T a,T b,T c)	
{
	cout <b?a:b;
}

int main()
{
	int a = 1;
	int b = 2;
	Max(a,b);	//C++编译器优先考虑普通函数
	Max<>(a,b);	//可以通过空模板实参列表的语言限定编译器只通过模板匹配
	Max(3.5,4.4);	//优先选择模板函数,因为模板函数能产生更好的匹配
	Max(3.5,4.4,6.8);//只有函数模板能匹配
	Max('A',66);	//普通函数可以进行隐式转换


	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp  && ./run 
1 2 我是普通函数
1 2 我是2号函数模板
3.5 4.4 我是2号函数模板
3.5 4.4 我是3号函数模板,会调用两次max函数
3.5 4.4 我是2号函数模板
4.4 6.8 我是2号函数模板
65 66 我是普通函数
chunli@http://990487026.blog.51cto.com~/c++$




C++是如何支持函数模板机制的?


C++:
#include
#include
#include
using namespace std;

//swap 在 C++是个库函数
template 
void my_swap(T &a,T &b)
{
	T c  = 0;
	c = a;
	a = b;
	b = c;
	cout <(a,b);

	char c = 'C';
	char d = 'D';
	my_swap(c,d);

	
	return 0;
}


编译成汇编:


	.file	"main.c"
.lcomm __ZStL8__ioinit,1,1
	.def	___main;	.scl	2;	.type	32;	.endef
	.text
	.globl	_main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
LFB1025:
	.cfi_startproc
	.cfi_personality 0,___gxx_personality_v0
	.cfi_lsda 0,LLSDA1025
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	andl	$-16, %esp
	subl	$32, %esp
	call	___main
	movl	$69, 28(%esp)
	movl	$20, 24(%esp)
	leal	24(%esp), %eax
	movl	%eax, 4(%esp)
	leal	28(%esp), %eax
	movl	%eax, (%esp)
LEHB0:
	call	__Z7my_swapIiEvRT_S1_
	movb	$67, 23(%esp)
	movb	$68, 22(%esp)
	leal	22(%esp), %eax
	movl	%eax, 4(%esp)
	leal	23(%esp), %eax
	movl	%eax, (%esp)
	call	__Z7my_swapIcEvRT_S1_
LEHE0:
	movl	$0, %eax
	jmp	L5
L4:
	movl	%eax, (%esp)
LEHB1:
	call	__Unwind_Resume
LEHE1:
L5:
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE1025:
	.def	___gxx_personality_v0;	.scl	2;	.type	32;	.endef
	.section	.gcc_except_table,"w"
LLSDA1025:
	.byte	0xff
	.byte	0xff
	.byte	0x1
	.uleb128 LLSDACSE1025-LLSDACSB1025
LLSDACSB1025:
	.uleb128 LEHB0-LFB1025
	.uleb128 LEHE0-LEHB0
	.uleb128 L4-LFB1025
	.uleb128 0
	.uleb128 LEHB1-LFB1025
	.uleb128 LEHE1-LEHB1
	.uleb128 0
	.uleb128 0
LLSDACSE1025:
	.text
	.section .rdata,"dr"
LC0:
	.ascii " \0"
LC1:
	.ascii " \316\322\312\307\272\257\312\375\304\243\260\345\12\0"
	.section	.text$_Z7my_swapIiEvRT_S1_,"x"
	.linkonce discard
	.globl	__Z7my_swapIiEvRT_S1_
	.def	__Z7my_swapIiEvRT_S1_;	.scl	2;	.type	32;	.endef
__Z7my_swapIiEvRT_S1_:
LFB1026:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	pushl	%ebx
	subl	$36, %esp
	.cfi_offset 3, -12
	movl	$0, -12(%ebp)
	movl	8(%ebp), %eax
	movl	(%eax), %eax
	movl	%eax, -12(%ebp)
	movl	12(%ebp), %eax
	movl	(%eax), %edx
	movl	8(%ebp), %eax
	movl	%edx, (%eax)
	movl	12(%ebp), %eax
	movl	-12(%ebp), %edx
	movl	%edx, (%eax)
	movl	12(%ebp), %eax
	movl	(%eax), %ebx
	movl	8(%ebp), %eax
	movl	(%eax), %eax
	movl	%eax, (%esp)
	movl	$__ZSt4cout, %ecx
	call	__ZNSolsEi
	subl	$4, %esp
	movl	$LC0, 4(%esp)
	movl	%eax, (%esp)
	call	__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
	movl	%ebx, (%esp)
	movl	%eax, %ecx
	call	__ZNSolsEi
	subl	$4, %esp
	movl	$LC1, 4(%esp)
	movl	%eax, (%esp)
	call	__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
	movl	-4(%ebp), %ebx
	leave
	.cfi_restore 5
	.cfi_restore 3
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE1026:
	.section	.text$_Z7my_swapIcEvRT_S1_,"x"
	.linkonce discard
	.globl	__Z7my_swapIcEvRT_S1_
	.def	__Z7my_swapIcEvRT_S1_;	.scl	2;	.type	32;	.endef
__Z7my_swapIcEvRT_S1_:
LFB1027:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	pushl	%ebx
	subl	$36, %esp
	.cfi_offset 3, -12
	movb	$0, -9(%ebp)
	movl	8(%ebp), %eax
	movzbl	(%eax), %eax
	movb	%al, -9(%ebp)
	movl	12(%ebp), %eax
	movzbl	(%eax), %edx
	movl	8(%ebp), %eax
	movb	%dl, (%eax)
	movl	12(%ebp), %eax
	movzbl	-9(%ebp), %edx
	movb	%dl, (%eax)
	movl	12(%ebp), %eax
	movzbl	(%eax), %eax
	movsbl	%al, %ebx
	movl	8(%ebp), %eax
	movzbl	(%eax), %eax
	movsbl	%al, %eax
	movl	%eax, 4(%esp)
	movl	$__ZSt4cout, (%esp)
	call	__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c
	movl	$LC0, 4(%esp)
	movl	%eax, (%esp)
	call	__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
	movl	%ebx, 4(%esp)
	movl	%eax, (%esp)
	call	__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c
	movl	$LC1, 4(%esp)
	movl	%eax, (%esp)
	call	__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
	addl	$36, %esp
	popl	%ebx
	.cfi_restore 3
	popl	%ebp
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE1027:
	.text
	.def	___tcf_0;	.scl	3;	.type	32;	.endef
___tcf_0:
LFB1033:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	subl	$8, %esp
	movl	$__ZStL8__ioinit, %ecx
	call	__ZNSt8ios_base4InitD1Ev
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE1033:
	.def	__Z41__static_initialization_and_destruction_0ii;	.scl	3;	.type	32;	.endef
__Z41__static_initialization_and_destruction_0ii:
LFB1032:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	subl	$24, %esp
	cmpl	$1, 8(%ebp)
	jne	L9
	cmpl	$65535, 12(%ebp)
	jne	L9
	movl	$__ZStL8__ioinit, %ecx
	call	__ZNSt8ios_base4InitC1Ev
	movl	$___tcf_0, (%esp)
	call	_atexit
L9:
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE1032:
	.def	__GLOBAL__sub_I_main;	.scl	3;	.type	32;	.endef
__GLOBAL__sub_I_main:
LFB1034:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	subl	$24, %esp
	movl	$65535, 4(%esp)
	movl	$1, (%esp)
	call	__Z41__static_initialization_and_destruction_0ii
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE1034:
	.section	.ctors,"w"
	.align 4
	.long	__GLOBAL__sub_I_main
	.ident	"GCC: (rev2, Built by MinGW-builds project) 4.8.0"
	.def	__Unwind_Resume;	.scl	2;	.type	32;	.endef
	.def	__ZNSolsEi;	.scl	2;	.type	32;	.endef
	.def	__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc;	.scl	2;	.type	32;	.endef
	.def	__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c;	.scl	2;	.type	32;	.endef
	.def	__ZNSt8ios_base4InitD1Ev;	.scl	2;	.type	32;	.endef
	.def	__ZNSt8ios_base4InitC1Ev;	.scl	2;	.type	32;	.endef
	.def	_atexit;	.scl	2;	.type	32;	.endef





类模板

类模板用于实现类所需数据的类型参数化 

类模板在表示如数组、表、图等数据结构显得特别重要,


类模板的定义

类末班的使用


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
#include
#include
using namespace std;
template 
class A
{
public:
	A(T a)
	{
		this->a = a;
	}
	void printf()
	{
		cout << "a= "<a(11);	//模板类是抽象类 必须指定具体的类型,告诉编译器给我分配多少内存
	a.printf();
	
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run 
a= 11
chunli@http://990487026.blog.51cto.com~/c++$


类模板做函数的参数


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
#include
#include
using namespace std;
template 
class A
{
public:
	A(T a)
	{
		this->a = a;
	}
	void printf()
	{
		cout << "a= "< &a)//这里如果来个变量,会进行拷贝构造,引用就不会
{
	a.printf();
}

int main()
{
	A   a(11);	//定义一个变量
	A   b(22),c(33);
	useA(a);	//来模板做函数的参数
	useA(b);
	useA(c);
	
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run 
a= 11
a= 22
a= 33
chunli@http://990487026.blog.51cto.com~/c++$



类模板的派生成普通类


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
#include
#include
using namespace std;
template 
class A
{
public:
	A(T a)
	{
		this->a = a;
	}
	void printf()
	{
		cout << "a= "<
{
public:
	B(int a,int b):A(a)
	{
		this->b = b;
	}
	void printf()
	{
		cout << "a="<




模板类的派生成模板类


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
#include
#include
using namespace std;
template 
class A
{
public:
	A(T a)
	{
		this->a = a;
	}
	void printf()
	{
		cout << "a= "<
class C:public A
{
public:
	C(T c,T a):A(a)
	{
		this->c = a;
	}
	void printf()
	{
		cout << "c=" << c<< endl;
	}
private:
	T c;
};


int main()
{
	C  c1(1,2); 	c1.printf();
	C  c2(66,65); 	c2.printf();
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run 
c=2
c=A
chunli@http://990487026.blog.51cto.com~/c++$




复数类,所有函数都写在类的内部,运算符重载热身


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
#include
#include
using namespace std;

class Complex
{
	friend ostream& operator<<(ostream &out,Complex &c);
public:
	Complex(int a = 0,int b = 0)
	{
		this->a = a;
		this->b = b;
	}
	void printf()
	{
		cout << "a= "<a=" << this->a << " c2.a="<b=" << this->b << " c2.b="<a + c2.a,this->b+c2.b);
		return tmp;
	}
protected:
	int a;
	int b;
};

//为输出类的信息,实现操作符重载
ostream& operator<<(ostream &out,Complex &c)
{
	cout <<"a="<





复数类,所有函数都写在类的内部, 类模板


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
#include
#include
using namespace std;

/*
	运算符重载的正规写法
	重载<< >> 只能用友元函数,其他操作符都要写成成员函数,不要滥用友元函数
*/

template 
class Complex
{
	//为输出类的信息,实现操作符重载
	friend ostream& operator<<(ostream &out,Complex &c)
	{
		cout << "进入<<操作符重载 -> ";
        	out <<"a="<a = a;
		this->b = b;
	}
	void printf()
	{
		cout << "a= "<a=" << this->a << " c2.a="<b=" << this->b << " c2.b="<a + c2.a,this->b+c2.b);
		return tmp;
	}
protected:
	T a;
	T b;
};

/*
ostream& operator<<(ostream &out,Complex &c)
{
	cout <<"a="< c1(2,4);
	Complex c2(3,5);
	Complex c3 = c1 +c2;	c3.printf();
	cout << c3 << endl;	//在得不到cout的源代码下,只能使用友元函数
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run 
a= 5 b=9
进入<<操作符重载 -> a=5 b=9 
chunli@http://990487026.blog.51cto.com~/c++$




【演示】滥用友元函数的后果--正常的代码准备


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
#include
#include
using namespace std;

/*
	运算符重载的正规写法
	重载<< >> 只能用友元函数,其他操作符都要写成成员函数,不要滥用友元函数
*/

template 
class Complex
{
	//为输出类的信息,实现操作符重载
	friend ostream& operator<<(ostream &out,Complex &c)
	{
		cout << "进入<<操作符重载 -> ";
        	out <<"a="<a = a;
		this->b = b;
	}
	void printf()
	{
		cout << "a= "<a=" << this->a << " c2.a="<b=" << this->b << " c2.b="<a + c2.a,this->b+c2.b);
		return tmp;
	}
protected:
	T a;
	T b;
};

/*
ostream& operator<<(ostream &out,Complex &c)
{
	cout <<"a="< c1(2,4);
	Complex c2(3,5);
	Complex c4 = MySub(c1,c2);
	cout << c4 << endl;
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run 
进入MySub 函数  
进入<<操作符重载 -> a=-1 b=-1 
chunli@http://990487026.blog.51cto.com~/c++$


复数类,所有函数都写在类的内部(一个CPP中):问题抛出


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
#include
#include
using namespace std;

template 
class Complex
{
	//为输出类的信息,实现操作符重载
	friend ostream& operator<<(ostream &out,Complex &c);
	friend Complex MySub(Complex& c1,Complex&c2)
	{
		cout << "进入MySub 函数  \n";
		Complex tmp(c1.a - c2.a,c1.b - c2.b);
		return tmp;
	}

public:
	Complex (T a ,T b);
	void printf();
	Complex operator+(Complex &c2);
protected:
	T a;
	T b;
};


/*
	问题的本质是:模板是两次编译,
	第一次生成的函数头 和第二次生成的不一样
*/
template 
ostream& operator<<(ostream &out,Complex &c)
{
	out <<"a="<
//函数的返回的类型要具体化
//函数的实参也要具体化
Complex Complex:: operator+(Complex &c2)
{
	cout << "进入外部 operator+ \n";
	//Complex tmp(this->a + c2.a,this->b+c2.b);
	Complex tmp(this->a + c2.a,this->b+c2.b);
	return tmp;
}

template 
Complex::Complex (T a ,T b)
{
	cout << "进入外部Complex函数 a=" <a = a;
	this->b = b;
}

template 
void Complex::printf()
{
	cout << "进入外部printf函数  ";
	cout << "a="< c1(2,4);	c1.printf();
	Complex c2(3,5);
	Complex c3 = c1 + c2;
	Complex c4 = MySub(c1,c2);
	cout << c4 << endl;
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run 
main.cpp:10:52: warning: friend declaration ‘std::ostream& operator<<(std::ostream&, Complex&)’ declares a non-template function [-Wnon-template-friend]
friend ostream& operator<<(ostream &out,Complex &c);

chunli@http://990487026.blog.51cto.com~/c++$


当模板函数遇到友元函数,问题解决:


chunli@http://990487026.blog.51cto.com~/c++$ cat haha.cpp 
#include
using namespace std;

template 
class Complex
{
	friend std::ostream& operator << (std::ostream& os, const Complex& c);
public:
	Complex(T a, T b);
protected:
	T a;
	T b;
};

template 
std::ostream& operator <<(std::ostream& os, const Complex& c)
{
	os << "a="<
Complex::Complex(T a, T b)
{
	cout << "进入外部Complex函数 a=" << a << " b=" << b << endl;;
	this->a = a;
	this->b = b;
}

int main()
{
	Complex c1(2, 4);
	cout << c1 << endl;
	return 0;
}

VS下编译,通过:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2.exe
进入外部Complex函数 a=2 b=4
a=2 b= 4

Linux GCC 编译 不通过:

chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run haha.cpp && ./run 
haha.cpp: In instantiation of ‘class Complex’:
haha.cpp:33:17:   required from here
haha.cpp:7:23: error: template-id ‘operator<< ’ for ‘std::ostream& operator<<(std::ostream&, const Complex&)’ does not match any template declaration
  friend std::ostream& operator << (std::ostream& os, const Complex& c);
                       ^
chunli@http://990487026.blog.51cto.com~/c++$



滥用友元函数的后果


#include
using namespace std;


template 
class Complex;
template 
Complex MySub(Complex &c1, Complex &c2);


template 
class Complex
{
	friend std::ostream& operator << (std::ostream& os, const Complex& c);
	friend Complex MySub(Complex &c1, Complex &c2);

public:
	Complex(T a, T b);
protected:
	T a;
	T b;
};

template 
Complex MySub(Complex &c1, Complex &c2)
{
	Complex  tmp(c1.a - c2.a, c1.b - c2.b);
	return tmp;
}


template 
std::ostream& operator <<(std::ostream& os, const Complex& c)
{
	os << "a="<
Complex::Complex(T a, T b)
{
	cout << "进入外部Complex函数 a=" << a << " b=" << b << endl;;
	this->a = a;
	this->b = b;
}

int main()
{
	Complex c1(2, 4);	
	Complex c2(1, 2);
	Complex c3 = MySub(c1,c2);
	cout << c3 << endl;
	return 0;
}





Linux GCC编译不通过



VS 编译OK
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2.exe
进入外部Complex函数 a=2 b=4
进入外部Complex函数 a=1 b=2
进入外部Complex函数 a=1 b=2
a=1 b= 2





【结论】:不需要友元函数,不要用友元函数




【结论】模板类的cpp文件 与 .h头文件分开写的时候,要把cpp文件也包含进来



当类模板中有static成员变量时;

从类模板的实质分析,编译器你会自动为我们写成两个类


chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
#include
using namespace std;

template 
class A
{
public:
	static T a;
	
};

template 
T A::a = 0;

int main()
{
	A a1;	a1.a++;
	A a2;	a2.a++;
	A a3;	a3.a++;
	cout << "a3="< b1;	b1.a += 3.5;
	A b2;	b2.a += 3.5;
	A b3;	b3.a += 3.5;
	cout << "b3="<



类模板 数组案例

1主函数:



#define _CRT_SECURE_NO_WARNINGS
#include 
using namespace std;

#include "MyVector.cpp"

void main()
{
	MyVector myv1(10);

	for (int i = 0; i myv2 = myv1;
	for (int i = 0; i


2头文件:MyVector


#include 
using namespace std;

template 
class MyVector
{

	friend ostream & operator<< (ostream &out, const MyVector &obj);
public:
	MyVector(int size = 0);  //构造函数
	MyVector(const MyVector &obj); // 拷贝构造函数
	~MyVector(); //析构函数

public:

	T& operator[] (int index);
	// a3 = a2 = a1;
	MyVector &operator=(const MyVector &obj);



public:
	int getLen()
	{
		return m_len;
	}

protected:
	T *m_space;
	int m_len;
};


类函数实现文件 MyVector.cpp


#include 
using namespace std;
#include "MyVector.h"

template 
ostream & operator<<(ostream &out, const MyVector &obj)
{
	for (int i = 0; i myv1(10);
template 
MyVector::MyVector(int size)  //构造函数
{
	m_space = new T[size];
	m_len = size;
}


//MyVector myv2  = myv1;
template 
MyVector::MyVector(const MyVector &obj) // 拷贝构造函数
{
	//根据myv1的大小分配内存
	m_len = obj.m_len;
	m_space = new T[m_len];

	//copy数据
	for (int i = 0; i
MyVector::~MyVector() //析构函数
{
	if (m_space != NULL)
	{
		delete[] m_space;
		m_space = NULL;
		m_len = 0;
	}
}

template 
T& MyVector::operator[] (int index)
{
	return m_space[index];
}


// a3 = a2 = a1;
template 
MyVector &  MyVector::operator=(const MyVector &obj)
{
	//先把a2的旧的内存释放掉

	if (m_space != NULL)
	{
		delete[] m_space;
		m_space = NULL;
		m_len = 0;
	}

	//根据a1分配内存 
	m_len = obj.m_len;
	m_space = new T[m_len];

	//copy数据
	for (int i = 0; i


VS环境编译运行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2.exe
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10

hello...
请按任意键继续. . .






类模板 结构体案例

类函数实现文件 MyVector.h 内容不变

类函数实现文件 MyVector.cpp内容不变

主函数修改为:

main.cpp



#define _CRT_SECURE_NO_WARNINGS
#include 
using namespace std;

#include "MyVector.cpp"

class Teacher
{
public:
	Teacher()
	{
		age = 33;
		strcpy(name, "");
	}

	Teacher(char *name, int age)
	{
		this->age = age;
		strcpy(this->name, name);
	}
	void printT()
	{
		cout << name << ", " << age << endl;
	}
private:
	int age;
	char name[32];
};

void main()
{
	Teacher t1("t1", 31), t2("t2", 32), t3("t3", 33), t4("t4", 34);

	MyVector tArray(4);

	tArray[0] = t1;
	tArray[1] = t2;
	tArray[2] = t3;
	tArray[3] = t4;

	for (int i = 0; i<4; i++)
	{
		Teacher tmp = tArray[i];
		tmp.printT();
	}
}


VS编译运行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2.exe
t1, 31
t2, 32
t3, 33
t4, 34
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>



作业:

在上面这个程序的基础之上:

1  优化Teacher类, 属性变成 char *panme, 购置函数里面 分配内存

2  优化Teacher类,析构函数 释放panme指向的内存空间

3  优化Teacher类,避免浅拷贝 重载= 重写拷贝构造函数 

4  优化Teacher类,在Teacher增加 << 

5  在模板数组类中,存int char Teacher Teacher*(指针类型)