【本文謝絕轉載】
【泛型程式設計】
函數模闆
為什麼會有函數模闆
現象:
函數的業務邏輯一樣
函數的參數類型不一樣
【最常用】函數模闆 顯式的調用
【不常用】類型推導
多個參數,參數定義了必須要用
函數模闆,實作int類型數組,char字元串排序:
函數模闆 與 普通函數的本質差別
函數模闆 和 普通函數在一起 的調用型研究:
C++是如何支援函數模闆機制的?
函數模闆機制結論
類模闆
類模闆的定義
類模闆做函數的參數
類模闆的派生成普通類
模闆類的派生成模闆類
複數類,所有函數都寫在類的内部,運算符重載熱身
複數類,所有函數都寫在類的内部, 類模闆
【示範】濫用友元函數的後果--正常的代碼準備
複數類,所有函數都寫在類的内部(一個CPP中):問題抛出
當模闆函數遇到友元函數,問題解決:
濫用友元函數的後果
【結論】:不需要友元函數,不要用友元函數
【結論】模闆類的cpp檔案 與 .h頭檔案分開寫的時候,要把cpp檔案也包含進來
當類模闆中有static成員變量時;從類模闆的實質分析,編譯器你會自動為我們寫成兩個類
類模闆 數組案例
類模闆 結構體案例
作業:
-------------------------------------------------------------------------
【為什麼會有函數模闆】
chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp
#include<iostream>
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 <<a <<" "<< b << endl;
char c = 'A';
char d = 'B';
swap2(c,d);
cout <<c <<" "<< d << endl;
return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run
20 10
B A
chunli@http://990487026.blog.51cto.com~/c++$
現象:
函數的業務邏輯一樣
函數的參數類型不一樣
【最常用】函數模闆 顯式的調用
chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp
#include<iostream>
using namespace std;
//template //告訴編譯器,我要開始泛型程式設計了
template <typename T>
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 << endl;
myswap<int>(a,b);
cout <<a <<" "<< b << endl;
char c = 'A';
char d = 'B';
cout <<c <<" "<< d << endl;
myswap<char>(c,d);
cout <<c <<" "<< d << endl;
return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run
10 20
20 10
A B
B A
chunli@http://990487026.blog.51cto.com~/c++$
【不常用】類型推導
chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp
#include<iostream>
using namespace std;
//template //告訴編譯器,我要開始泛型程式設計了
template <typename T>
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 << endl;
myswap(a,b);
cout <<a <<" "<< b << endl;
char c = 'A';
char d = 'B';
cout <<c <<" "<< d << endl;
myswap(c,d);
cout <<c <<" "<< d << endl;
return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run
10 20
20 10
A B
B A
chunli@http://990487026.blog.51cto.com~/c++$
多個參數,參數定義了必須要用
chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp
#include<iostream>
using namespace std;
//template //告訴編譯器,我要開始泛型程式設計了
template <typename T,typename str>
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 <<a <<" "<< b << endl;
myswap(a,b,"Hello");
cout <<a <<" "<< b << endl;
char c = 'A';
char d = 'B';
cout <<c <<" "<< d << endl;
myswap(c,d,"Linux");
cout <<c <<" "<< d << endl;
return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run
10 20
Hello
20 10
A B
Linux
B A
chunli@http://990487026.blog.51cto.com~/c++$
函數模闆,實作int類型數組,char字元串排序:
chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
template <typename T1,typename T2>
void select_sort(T1 array[],T2 len)
{
if(array == NULL)
{
return ;
}
for(int i=0;i<len-1;i++)
{
int min = i;
for(int j=i+1;j<len;j++)
{
if(array[min] > array[j])
{
min = j ;
}
}
int tmp = array[i];
array[i] = array[min];
array[min]= tmp;
}
}
template <typename T1,typename T2>
void print_arr(T1 array[],T2 len)
{
if(array == NULL)
{
return ;
}
for(int i=0;i<len;i++)
{
cout << array[i] << " ";
}
cout << "\n";
}
int main()
{
int arr[]= {2,1,4,3,6,5,8,7,0,9};
int n =sizeof(arr)/sizeof(int);
select_sort<int ,int>(arr,n);
print_arr<int,int>(arr,n);
char str[]= "Hello,Linux!";
int m = strlen(str);
select_sort<char ,int>(str,m);
print_arr<char ,int>(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<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
//swap 在 C++是個庫函數
template <typename T>
void my_swap(T &a,T &b)
{
cout <<a << " " <<b<< " 我是函數模闆\n";
}
void my_swap(int a,char b)
{
cout << a << " " << b << " 我是普通函數\n";
}
int main()
{
int a = 69;
char c = 'A';
my_swap(a,c);
my_swap(c,a); //會隐式的自動轉換
my_swap(a,a); //[本質的差別]函數模闆,将嚴格按照類型比對,不會自動轉換
return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run
69 A 我是普通函數
65 E 我是普通函數
69 69 我是函數模闆
chunli@http://990487026.blog.51cto.com~/c++$
函數模闆 和 普通函數在一起 的調用型研究:
【補充】在同一作用域,可以發生函數重載
chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp
/*
1 函數模闆可以像普通函數一樣被重載
2 C++編譯器優先考慮普通函數
3 如果函數模闆可以産生一個更好的比對,那麼選擇模闆
4 可以通過空模闆實參清單的語言限定編譯器隻通過模闆比對
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
template <typename T>
T Max(T a,T b) //不要引用類型
{
cout <<a << " " <<b<< " 我是2号函數模闆\n";
return a>b?a:b;
}
template <typename T>
T Max(T a,T b,T c)
{
cout <<a << " " <<b<< " 我是3号函數模闆,會調用兩次max函數\n";
return Max(Max(a,b),c);
}
int Max(int a,int b)
{
cout << a << " " << b << " 我是普通函數\n";
return a>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<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
//swap 在 C++是個庫函數
template <typename T>
void my_swap(T &a,T &b)
{
T c = 0;
c = a;
a = b;
b = c;
cout <<a << " " <<b<< " 我是函數模闆\n";
}
int main()
{
int a = 69;
int b = 20;
my_swap<int>(a,b);
char c = 'C';
char d = 'D';
my_swap<char>(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<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
template <typename T>
class A
{
public:
A(T a)
{
this->a = a;
}
void printf()
{
cout << "a= "<<a <<endl;
}
private:
T a;
};
int main()
{
//類本身就是一個抽象的
A <int>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<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
template <typename T>
class A
{
public:
A(T a)
{
this->a = a;
}
void printf()
{
cout << "a= "<<a <<endl;
}
private:
T a;
};
//類模闆做函數的參數
void useA(A<int> &a)//這裡如果來個變量,會進行拷貝構造,引用就不會
{
a.printf();
}
int main()
{
A <int> a(11); //定義一個變量
A <int> 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<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
template <typename T>
class A
{
public:
A(T a)
{
this->a = a;
}
void printf()
{
cout << "a= "<<a <<endl;
}
protected:
T a;
};
class B :public A<int>
{
public:
B(int a,int b):A<int>(a)
{
this->b = b;
}
void printf()
{
cout << "a="<<a << " b=" << b << " \n";
}
private:
int b;
};
int main()
{
B b(1,2);
b.printf();
return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run
a=1 b=2
模闆類的派生成模闆類
chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
template <typename T>
class A
{
public:
A(T a)
{
this->a = a;
}
void printf()
{
cout << "a= "<<a <<endl;
}
protected:
T a;
};
template <typename T>
class C:public A<T>
{
public:
C(T c,T a):A<T>(a)
{
this->c = a;
}
void printf()
{
cout << "c=" << c<< endl;
}
private:
T c;
};
int main()
{
C <int> c1(1,2); c1.printf();
C <char> 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<iostream>
#include<stdio.h>
#include<string.h>
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 <<" b="<<b <<endl;
}
Complex operator+(Complex &c2)
{
//cout << "this->a=" << this->a << " c2.a="<<c2.a<<endl;
//cout << "this->b=" << this->b << " c2.b="<<c2.b<<endl;
Complex tmp(this->a + c2.a,this->b+c2.b);
return tmp;
}
protected:
int a;
int b;
};
//為輸出類的資訊,實作操作符重載
ostream& operator<<(ostream &out,Complex &c)
{
cout <<"a="<<c.a<<" b="<<c.b<< " ";
return out;
}
int main()
{
Complex 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<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
/*
運算符重載的正規寫法
重載<< >> 隻能用友元函數,其他操作符都要寫成成員函數,不要濫用友元函數
*/
template <typename T>
class Complex
{
//為輸出類的資訊,實作操作符重載
friend ostream& operator<<(ostream &out,Complex &c)
{
cout << "進入<<操作符重載 -> ";
out <<"a="<<c.a<<" b="<<c.b<< " ";
return out;
}
public:
Complex (T a ,T b)
{
this->a = a;
this->b = b;
}
void printf()
{
cout << "a= "<<a <<" b="<<b <<endl;
}
Complex operator+(Complex &c2)
{
//cout << "this->a=" << this->a << " c2.a="<<c2.a<<endl;
//cout << "this->b=" << this->b << " c2.b="<<c2.b<<endl;
Complex tmp(this->a + c2.a,this->b+c2.b);
return tmp;
}
protected:
T a;
T b;
};
/*
ostream& operator<<(ostream &out,Complex &c)
{
cout <<"a="<<c.a<<" b="<<c.b<< " ";
return out;
}
*/
int main()
{
//要把模闆類具體化才能配置設定記憶體
Complex<int> c1(2,4);
Complex<int> c2(3,5);
Complex<int> 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<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
/*
運算符重載的正規寫法
重載<< >> 隻能用友元函數,其他操作符都要寫成成員函數,不要濫用友元函數
*/
template <typename T>
class Complex
{
//為輸出類的資訊,實作操作符重載
friend ostream& operator<<(ostream &out,Complex &c)
{
cout << "進入<<操作符重載 -> ";
out <<"a="<<c.a<<" b="<<c.b<< " ";
return out;
}
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)
{
this->a = a;
this->b = b;
}
void printf()
{
cout << "a= "<<a <<" b="<<b <<endl;
}
Complex operator+(Complex &c2)
{
//cout << "this->a=" << this->a << " c2.a="<<c2.a<<endl;
//cout << "this->b=" << this->b << " c2.b="<<c2.b<<endl;
Complex tmp(this->a + c2.a,this->b+c2.b);
return tmp;
}
protected:
T a;
T b;
};
/*
ostream& operator<<(ostream &out,Complex &c)
{
cout <<"a="<<c.a<<" b="<<c.b<< " ";
return out;
}
*/
int main()
{
//要把模闆類具體化才能配置設定記憶體
Complex<int> c1(2,4);
Complex<int> c2(3,5);
Complex<int> 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<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
template <typename T>
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 <typename T>
ostream& operator<<(ostream &out,Complex &c)
{
out <<"a="<<c.a<<" b="<<c.b<< " ";
return out;
}
template <typename T>
//函數的傳回的類型要具體化
//函數的實參也要具體化
Complex<T> Complex<T>:: operator+(Complex<T> &c2)
{
cout << "進入外部 operator+ \n";
//Complex<T> tmp(this->a + c2.a,this->b+c2.b);
Complex tmp(this->a + c2.a,this->b+c2.b);
return tmp;
}
template <typename T>
Complex<T>::Complex (T a ,T b)
{
cout << "進入外部Complex函數 a=" <<a<<" b="<< b<< endl; ;
this->a = a;
this->b = b;
}
template <typename T>
void Complex<T>::printf()
{
cout << "進入外部printf函數 ";
cout << "a="<<a <<" b="<<b <<endl;
}
int main()
{
Complex<int> c1(2,4); c1.printf();
Complex<int> c2(3,5);
Complex<int> c3 = c1 + c2;
Complex<int> 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<T>&)’ 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<iostream>
using namespace std;
template <typename T>
class Complex
{
friend std::ostream& operator << <T>(std::ostream& os, const Complex<T>& c);
public:
Complex(T a, T b);
protected:
T a;
T b;
};
template <typename T>
std::ostream& operator <<(std::ostream& os, const Complex<T>& c)
{
os << "a="<<c.a <<" b= "<< c.b<<std::endl;
return os;
}
template <typename T>
Complex<T>::Complex(T a, T b)
{
cout << "進入外部Complex函數 a=" << a << " b=" << b << endl;;
this->a = a;
this->b = b;
}
int main()
{
Complex<int> 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<int>’:
haha.cpp:33:17: required from here
haha.cpp:7:23: error: template-id ‘operator<< <int>’ for ‘std::ostream& operator<<(std::ostream&, const Complex<int>&)’ does not match any template declaration
friend std::ostream& operator << <T>(std::ostream& os, const Complex<T>& c);
^
chunli@http://990487026.blog.51cto.com~/c++$
濫用友元函數的後果
#include<iostream>
using namespace std;
template <typename T>
class Complex;
template <typename T>
Complex<T> MySub(Complex<T> &c1, Complex<T> &c2);
template <typename T>
class Complex
{
friend std::ostream& operator << <T>(std::ostream& os, const Complex<T>& c);
friend Complex<T> MySub<T>(Complex<T> &c1, Complex<T> &c2);
public:
Complex(T a, T b);
protected:
T a;
T b;
};
template <typename T>
Complex<T> MySub(Complex<T> &c1, Complex <T>&c2)
{
Complex <T> tmp(c1.a - c2.a, c1.b - c2.b);
return tmp;
}
template <typename T>
std::ostream& operator <<(std::ostream& os, const Complex<T>& c)
{
os << "a="<<c.a <<" b= "<< c.b<<std::endl;
return os;
}
template <typename T>
Complex<T>::Complex(T a, T b)
{
cout << "進入外部Complex函數 a=" << a << " b=" << b << endl;;
this->a = a;
this->b = b;
}
int main()
{
Complex<int> c1(2, 4);
Complex<int> c2(1, 2);
Complex<int> c3 = MySub<int>(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<iostream>
using namespace std;
template <typename T>
class A
{
public:
static T a;
};
template <typename T>
T A<T>::a = 0;
int main()
{
A<int> a1; a1.a++;
A<int> a2; a2.a++;
A<int> a3; a3.a++;
cout << "a3="<<a3.a << "\n";
A<double> b1; b1.a += 3.5;
A<double> b2; b2.a += 3.5;
A<double> b3; b3.a += 3.5;
cout << "b3="<<b3.a << "\n";
return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$ g++ -g -o run main.cpp && ./run
a3=3
b3=10.5
chunli@http://990487026.blog.51cto.com~/c++$
類模闆 數組案例
1主函數:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include "MyVector.cpp"
void main()
{
MyVector<int> myv1(10);
for (int i = 0; i<myv1.getLen(); i++)
{
myv1[i] = i + 1;
cout << myv1[i] << " ";
}
cout << endl;
MyVector<int> myv2 = myv1;
for (int i = 0; i<myv2.getLen(); i++)
{
cout << myv2[i] << " ";
}
cout << myv2 << endl;
cout << "hello..." << endl;
system("pause");
return;
}
2頭檔案:MyVector
#include <iostream>
using namespace std;
template <typename T>
class MyVector
{
friend ostream & operator<< <T>(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 <iostream>
using namespace std;
#include "MyVector.h"
template <typename T>
ostream & operator<<(ostream &out, const MyVector<T> &obj)
{
for (int i = 0; i<obj.m_len; i++)
{
out << obj.m_space[i] << " ";
//out << t1;
}
out << endl;
return out;
}
//MyVector<int> myv1(10);
template <typename T>
MyVector<T>::MyVector(int size) //構造函數
{
m_space = new T[size];
m_len = size;
}
//MyVector<int> myv2 = myv1;
template <typename T>
MyVector<T>::MyVector(const MyVector &obj) // 拷貝構造函數
{
//根據myv1的大小配置設定記憶體
m_len = obj.m_len;
m_space = new T[m_len];
//copy資料
for (int i = 0; i<m_len; i++)
{
m_space[i] = obj.m_space[i];
}
}
template <typename T>
MyVector<T>::~MyVector() //析構函數
{
if (m_space != NULL)
{
delete[] m_space;
m_space = NULL;
m_len = 0;
}
}
template <typename T>
T& MyVector<T>::operator[] (int index)
{
return m_space[index];
}
// a3 = a2 = a1;
template <typename T>
MyVector<T> & MyVector<T>::operator=(const MyVector<T> &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<m_len; i++)
{
m_space[i] = obj[i];
}
return *this; // a2 = a1; 傳回給a2 的自身
}
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 <iostream>
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<Teacher> 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*(指針類型)