CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛

C++进阶之函数模板——类模板(二)-源码交易平台丞旭猿

2.类模板

2.1为什么需要类模板

类模板与函数模板的定义和使用类似,我们已经进行了介绍。 有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,如下面语句声明了一个类:

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

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

这些数据结构的表示和算法不受所包含的元素类型的影响

2.2单个类模板语法

1 //类的类型参数化 抽象的类 2 //单个类模板 3 template 4 class A
5 { 6 public: 7 A(T t) 8 { 9 this->t = t;10 }11 12 T &getT()13 {14 return t;15 }16 protected:17 public:18 T t;19 };20 void main()21 {22 //模板了中如果使用了构造函数,则遵守以前的类的构造函数的调用规则23 A a(100);
24 a.getT();25 printAA(a);26 return ;27 }

2.3继承中的类模板语法

案例:

1 include 2 using namespace std; 3 //A编程模板类--类型参数化 4 /* 5 类模板的定义 类模板的使用 类模板做函数参数 6 */ 7 template  8 class A 9 {10 public:11 A(T a = 0)12 {13 this->a = a;14 }15 public:16 void printA()17 {18 cout << "a:" << a << endl;19 }20 protected:21 T a;22 private:23 24 };25 //从模板类派生时,需要具体化模板类,C++编译器需要知道父类的数据类型是什么样子的26 //要知道父类所占的内存多少27 class B :public A28 {29 public:30 B(int a =10, int b =20):A(a)31 {32 this->b = b;33 }34 void printB()35 {36 cout << "a:" << a << "b:" << b << endl;37 }38 protected:39 private:40 int b;41 42 };43 //从模板类派生模板类44 template 45 class C :public A46 {47 48 public:49 C(T c,T a) : A(a)50 {51 this->c = c;52 }53 void printC()54 {55 cout << "c:" << c << endl;56 }57 protected:58 T c;59 private:60 61 };62 63 void main()64 {65 //B b1(1, 2);66 //b1.printB();67 C c1(1,2);68 c1.printC();69 }

2.4类模板的基础语法

你还在为没有学习平台而苦恼吗?你还在为没有学习资料而烦心吗?你还在为没人指导而担忧吗?可以私信小编 C++,为你提供学习的平台和资料。

1 include 2 using namespace std; 3 //A编程模板类--类型参数化 4 /* 5 类模板的定义 类模板的使用 类模板做函数参数 6 */ 7 template  8 class A 9 {10 public:11 A(T a = 0)12 {13 this->a = a;14 }15 public:16 void printA()17 {18 cout << "a:" << a << endl;19 }20 protected:21 private:22 T a;23 };24 //参数 C++编译器具体的类25 void UseA(A &a)26 {27 a.printA();28 }29 void main()30 {31 //模板类本身就是抽象的,具体的类,具体的变量32 A a1(11),a2(22),a3(33);//模板类是抽象的, 需要类型具体化33 //a1.printA();34 35 UseA(a1);36 UseA(a2);37 UseA(a3);38 }

2.5类模板语法知识体系梳理

1.所有的类模板函数写在类的内部

代码:

复数类:

1 include 2 using namespace std; 3 template  4 class Complex 5 { 6 public: 7 friend Complex MySub(Complex &c1, Complex &c2) 8 { 9 Complex tmp(c1.a-c2.a, c1.b-c2.b);10 return tmp;11 }12 13 friend ostream & operator<< (ostream &out, Complex &c3)14 {15 out << c3.a << "+" << c3.b <<"i"<< endl;16 return out;17 }18 Complex(T a, T b)19 {20 this->a = a;21 this->b = b;22 }23 Complex operator+(Complex &c2)24 {25 Complex tmp(a + c2.a, b + c2.b);26 return tmp;27 }28 void printCom()29 {30 cout << "a:" << a << " b:" << b << endl;31 }32 protected:33 private:34 T a;35 T b;36 };37 38 /*39 重载运算符的正规写法:40 重载左移<< 右移>> 只能用友元函数,其他的运算符重载都要用成员函数,不要滥用友元函数41 */42 //ostream & operator<< (ostream &out, Complex &c3)43 //{44 // out<< "a:" << c3.a << " b:" << c3.b << endl;45 // return out;46 //}47 void main()48 {49 Complex c1(1,2);50 Complex c2(3, 4);51 52 Complex c3 = c1 + c2;//重载加号运算符53 54 c3.printCom();55 56 //重载左移运算符57 cout << c3 << endl;58 59 {60 Complex c4 = MySub(c1 , c2);61 62 cout << c4 << endl;63 }64 system("pause");65 }

2.所有的类模板函数写在类的外部,在一个cpp中

注意:

//构造函数 没有问题//普通函数 没有问题//友元函数:用友元函数重载 << >>// friend ostream& operator<<  (ostream &out, Complex &c3) ;//友元函数:友元函数不是实现函数重载(非 << >>)//1)需要在类前增加 类的前置声明 函数的前置声明
templateclass Complex; 
templateComplex mySub(Complex &c1, Complex &c2);//2)类的内部声明 必须写成:friend Complex mySub  (Complex &c1, Complex &c2);//3)友元函数实现 必须写成:
template
 Complex mySub(Complex &c1, Complex &c2)
{
Complex tmp(c1.a - c2.a, c1.b-c2.b);return tmp;
}//4)友元函数调用 必须写成Complex c4 = mySub(c1, c2);
cout<

结论:友元函数只用来进行 左移 友移操作符重载。

复数类:

代码:

include 2 using namespace std; 3 4 template 5 class Complex; 6 template 7 Complex mySub(Complex &c1, Complex &c2); 8 9 template 10 class Complex11 {12 public:13 friend Complex mySub (Complex &c1, Complex &c2);14 15 friend ostream & operator<< (ostream &out, Complex &c3);16 Complex(T a, T b);17 void printCom();18 Complex operator+(Complex &c2);19 Complex operator-(Complex &c2);20 21 protected:22 private:23 T a;24 T b;25 };26 27 //构造函数的实现,写在了外部28 template 29 Complex::Complex(T a, T b)30 {31 this->a = a;32 this->b = b;33 }34 35 template 36 void Complex::printCom()37 {38 cout << "a:" << a << " b:" << b << endl;39 }40 //成员函数实现加号运算符重载41 template 42 Complex Complex::operator+(Complex &c2)43 {44 Complex tmp(a + c2.a, b + c2.b);45 return tmp;46 }47 template 48 Complex Complex::operator-(Complex &c2)49 {50 Complex(a-c2.a,a-c2.b);51 return tmp;52 }53 //友元函数实现<<左移运算符重载54 55 /*56 严重性 代码 说明 项目 文件 行 禁止显示状态57 错误 C2768 operator <<: 非法使用显式模板参数 泛型编程课堂操练 
58 59 错误的本质:两次编译的函数头,第一次编译的函数头,和第二次编译的函数有不一样60 */61 template 62 ostream & operator<< (ostream &out, Complex &c3)//不加T63 {64 out << c3.a << "+" << c3.b << "i" << endl;65 return out;66 }67 68 69 //////////////////////////////////////////////////70 template 71 Complex mySub(Complex &c1, Complex &c2)72 {73 Complex tmp(c1.a - c2.a, c1.b - c2.b);74 return tmp;75 }76 77 void main()78 {79 Complex c1(1, 2);80 Complex c2(3, 4);81 82 Complex c3 = c1 + c2;//重载加号运算符83 84 c3.printCom();85 86 //重载左移运算符87 cout << c3 << endl;88 89 {90 Complex c4 = mySub(c1, c2);91 92 cout << c4 << endl;93 }94 system("pause");95 }

所有的类模板函数写在类的外部,在不同的.h和.cpp中

也就是类模板函数说明和类模板实现分开

//类模板函数

构造函数

普通成员函数

友元函数

用友元函数重载<<>>;

用友元函数重载非<< >>

//要包含.cpp

demo_09complex.cpp

1 include"demo_09complex.h" 2 include 3 using namespace std; 4 5 template  6 Complex::Complex(T a, T b) 7 { 8 this->a = a; 9 this->b = b;10 }11 12 template 13 void Complex::printCom()14 {15 cout << "a:" << a << " b:" << b << endl;16 }17 //成员函数实现加号运算符重载18 template 19 Complex Complex::operator+(Complex &c2)20 {21 Complex tmp(a + c2.a, b + c2.b);22 return tmp;23 }24 //template 25 //Complex Complex::operator-(Complex &c2)26 //{27 // Complex(a - c2.a, a - c2.b);28 // return tmp;29 //}30 template 31 ostream & operator<< (ostream &out, Complex &c3)//不加T32 {33 out << c3.a << "+" << c3.b << "i" << endl;34 return out;35 }36 37 38 //////////////////////////////////////////////////39 //template 40 //Complex mySub(Complex &c1, Complex &c2)41 //{42 // Complex tmp(c1.a - c2.a, c1.b - c2.b);43 // return tmp;44 //}

demo_09complex.h

1 pragma once 2 include 3 using namespace std; 4 template  5 class Complex 6 { 7 public: 8 //friend Complex mySub (Complex &c1, Complex &c2); 9 10 friend ostream & operator<< (ostream &out, Complex &c3);11 Complex(T a, T b);12 void printCom();13 Complex operator+(Complex &c2);14 //Complex operator-(Complex &c2);15 16 protected:17 private:18 T a;19 T b;20 };

demo_09complex_text.cpp

1 include"demo_09complex.h" 2 include"demo_09complex.cpp" 3 4 include 5 using namespace std; 6 7 void main() 8 { 9 Complex c1(1, 2);10 Complex c2(3, 4);11 12 Complex c3 = c1 + c2;//重载加号运算符13 14 c3.printCom();15 16 //重载左移运算符17 cout << c3 << endl;18 19 /*{20 Complex c4 = mySub(c1, c2);21 22 cout << c4 << endl;23 }*/24 system("pause");25 }

2.5总结

你还在为没有学习平台而苦恼吗?你还在为没有学习资料而烦心吗?你还在为没人指导而担忧吗?可以私信小编 C++,为你提供学习的平台和资料。

归纳以上的介绍,可以这样声明和使用类模板:

1) 先写出一个实际的类。由于其语义明确,含义清楚,一般不会出错。

2) 将此类中准备改变的类型名(如int要改变为float或char)改用一个自己指定的虚拟类型名(如上例中的numtype)。

3) 在类声明前面加入一行,格式为:

template

如:

template //注意本行末尾无分号

class Compare

{…}; //类体

4) 用类模板定义对象时用以下形式:

类模板名<实际类型名> 对象名;

类模板名<实际类型名> 对象名(实参表列);

如:

Compare cmp;

Compare cmp(3,7);

5) 如果在类模板外定义成员函数,应写成类模板形式:

template

函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表列) {…}

关于类模板的几点说明:

1) 类模板的类型参数可以有一个或多个,每个类型前面都必须加class,如:

template

class someclass

{…};

在定义对象时分别代入实际的类型名,如:

someclass obj;

2) 和使用类一样,使用类模板时要注意其作用域,只能在其有效作用域内用它定义对象。

3) 模板可以有层次,一个类模板可以作为基类,派生出派生模板类。

2.6类模板中的static关键字

  • 从类模板实例化的每个模板类有自己的类模板数据成员,该模板类的所有对象共享一个static数据成员

  • 和非模板类的static数据成员一样,模板类的static数据成员也应该在文件范围定义和初始化

  • 每个模板类有自己的类模板的static数据成员副本

1 include 2 using namespace std; 3 template  4 class AA 5 { 6 public: 7 static T m_a; 8 protected: 9 private:10 };11 template 12 T AA::m_a =0;13 void main()14 {15 AA a1, a2, a3;16 a1.m_a = 10;17 a2.m_a++;18 a3.m_a++;19 cout << AA::m_a << endl;20 21 AA b1, b2, b3;22 b1.m_a = a;23 b2.m_a++;24 b3.m_a++;25 cout << AA::m_a << endl;26 27 //m_a是每个类型的类,去使用,手工写两个类 int char28 system("pause");29 }

当类模板中出现static修饰的静态类成员的时候,我们只要按照正常理解就可以了。static的作用是将类的成员修饰成静态的,所谓的静态类成员就是指类的成员为类级别的,不需要实例化对象就可以使用,而且类的所有对象都共享同一个静态类成员,因为类静态成员是属于类而不是对象。那么,类模板的实现机制是通过二次编译原理实现的。c++编译器并不是在第一个编译类模板的时候就把所有可能出现的类型都分别编译出对应的类(太多组合了),而是在第一个编译的时候编译一部分,遇到泛型不会替换成具体的类型(这个时候编译器还不知道具体的类型),而是在第二次编译的时候再将泛型替换成具体的类型(这个时候编译器知道了具体的类型了)。由于类模板的二次编译原理再加上static关键字修饰的成员,当它们在一起的时候实际上一个类模板会被编译成多个具体类型的类,所以,不同类型的类模板对应的static成员也是不同的(不同的类),但相同类型的类模板的static成员是共享的(同一个类)。

2.7类模板在项目开发中的应用

小结

  • 模板是C++类型参数化的多态工具。C++提供函数模板和类模板。

  • 模板定义以模板说明开始。类属参数必须在模板定义中至少出现一次。

  • 同一个类属参数可以用于多个模板。

  • 类属参数可用于函数的参数类型、返回类型和声明函数中的变量。

  • 模板由编译器根据实际数据类型实例化,生成可执行代码。实例化的函数。

模板称为模板函数;实例化的类模板称为模板类。

  • 函数模板可以用多种方式重载。

  • 类模板可以在类层次中使用 。

训练题

1) 请设计一个数组模板类( MyVector ),完成对int、char、Teacher类型元素的管理。

需求

设计:

类模板 构造函数 拷贝构造函数 << [] 重载=操作符

a2=a1

实现

2) 请仔细思考:

a) 如果数组模板类中的元素是Teacher元素时,需要Teacher类做什么工作

b) 如果数组模板类中的元素是Teacher元素时,Teacher类含有指针属性哪?

1 class Teacher 2 { 3 friend ostream & operator<<(ostream &out, const Teacher &obj); 4 public: 5 Teacher(char *name, int age) 6 { 7 this->age = age; 8 strcpy(this->name, name); 9 }10 11 Teacher()12 {13 this->age = 0;14 strcpy(this->name, "");15 }16 17 private:18 int age;19 char name[32];20 };21 22 23 class Teacher24 {25 friend ostream & operator<<(ostream &out, const Teacher &obj);26 public:27 Teacher(char *name, int age)28 {29 this->age = age;30 strcpy(this->name, name);31 }32 33 Teacher()34 {35 this->age = 0;36 strcpy(this->name, "");37 }38 39 private:40 int age;41 char *pname;42 };

结论1: 如果把Teacher放入到MyVector数组中,并且Teacher类的属性含有指针,就是出现深拷贝和浅拷贝的问题。

结论2:需要Teacher封装的函数有:

1) 重写拷贝构造函数

2) 重载等号操作符

3) 重载左移操作符。

理论提高:

所有容器提供的都是值(value)语意,而非引用(reference)语意。容器执行插入元素的操作时,内部实施拷贝动作。所以STL容器内存储的元素必须能够被拷贝(必须提供拷贝构造函数)。

3) 请从数组模板中进行派生

1 //演示从模板类 派生 一般类 2 include "MyVector.cpp" 3 4 class MyArray01 : public MyVector 5 { 6 public: 7 MyArray01(int len) : MyVector(len) 8 { 9 ;10 }11 protected:12 private:13 };14 15 16 //演示从模板类 派生 模板类 //BoundArray 17 template 18 class MyArray02 : public MyVector19 {20 public:21 MyArray02(int len) : MyVector(len)22 {23 ;24 }25 protected:26 private:27 };28 测试案例:29 30 //演示 从模板类 继承 模板类31 void main()32 {33 MyArray02 dArray2(10);34 dArray2[1] = 3.15;35 36 }37 38 39 //演示 从模板类 继承 一般类40 void main11()41 {42 MyArray01 d_array(10);43 44 for (int i=0; i

作业:

封装你自己的数组类;设计被存储的元素为类对象;

思考:类对象的类,应该实现的功能。

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

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

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

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

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

zuoye.h

1 pragma once 2 3 template  4 class MyVector 5 { 6 7 //friend ostream & operator<< (ostream &out, const MyVector &obj); 8 public: 9 MyVector(int size = 0);//构造函数10 MyVector(const MyVector &obj);//copy构造函数11 ~MyVector();12 public:13 T& operator [](int index);14 MyVector &operator=(const MyVector &obj);15 16 17 int getLen()18 {19 return m_len;20 }21 protected:22 private:23 T *m_space;24 int m_len;25 26 };

zuoye_test12.cpp

1 include"zuoye.h" 2 include"zuoye12.cpp" 3 include 4 using namespace std; 5 class Teacher 6 { 7 public: 8 Teacher() 9 { 10 age = 33; 11 m_p = new char[1]; 12 strcpy(m_p, " "); 13 } 14 15 Teacher(char *name, int age) 16 { 17 this->age = age; 18 m_p = new char[strlen(name)+1]; 19 strcpy(this->m_p, name); 20 21 } 22 Teacher(const Teacher &obj) 23 { 24 m_p = new char[strlen(obj.m_p) + 1]; 25 strcpy(this->m_p, obj.m_p); 26 age = obj.age; 27 } 28 ~Teacher() 29 { 30 if (m_p!=NULL) 31 { 32 delete[] m_p; 33 m_p = NULL; 34 } 35 } 36 void printT() 37 { 38 cout << m_p << ", " << age; 39 } 40 public: 41 //重载<< == 42 friend ostream & operator<<(ostream &out,Teacher &t); 43 Teacher & operator=(const Teacher &obj)
44 { 45 //1 46 if (m_p!=NULL) 47 { 48 delete[] m_p; 49 m_p = NULL; 50 age = 33; 51 } 52 //2 53 m_p = new char[strlen(obj.m_p) + 1]; 54 age = obj.age; 55 //3 56 strcpy(this->m_p, obj.m_p); 57 return *this; 58 } 59 protected: 60 private: 61 int age; 62 //char name[32]; 63 char *m_p; 64 }; 65 ostream & operator<<(ostream &out, Teacher &t) 66 { 67 out << t.m_p << ", " << t.age << endl; 68 return out; 69 } 70 71 void main() 72 { 73 Teacher t1("t1", 31), t2("t2", 32); 74 75 MyVector Tarray(2); 76 77 Tarray[0] = &t1; 78 Tarray[1] = &t2; 79 for (int i = 0; i < 2; i++) 80 { 81 Teacher *tmp = Tarray[i]; 82 tmp->printT(); 83 } 84 system("pause"); 85 } 86 void main123() 87 { 88 Teacher t1("t1", 31), t2("t2", 32); 89 MyVector Tarray(2); 90 Tarray[0] = t1; 91 Tarray[1] = t2; 92 for (int i = 0; i < 2; i++) 93 { 94 Teacher tmp = Tarray[i]; 95 tmp.printT(); 96 } 97 system("pause"); 98 } 99 void main112()100 {101 MyVector myv1(10);102 myv1[0] = a;103 myv1[1] = b;104 myv1[2] = c;105 myv1[3] = d;106 myv1[4] = e;107 //cout << myv1;108 MyVector myv2 = myv1;109 }110 111 112 void main111()113 {114 MyVector myv1(10);115 for (int i = 0; i < myv1.getLen(); i++)116 {117 myv1[i] = i + 1;118 cout << myv1[i] << " ";119 }120 121 MyVector myv2 = myv1;122 for (int i = 0; i < myv2.getLen(); i++)123 {124 myv2[i] = i + 1;125 cout << myv2[i] << " ";126 }127 128 //cout << myv2 << endl;//重载左移运算符129 130 system("pause");131 }

zuoye12.cpp

1 include"zuoye.h" 2 include 3 using namespace std; 4 5 template  6 ostream & operator<<(ostream &out, const MyVector &obj) 7 { 8 for (int i = 0; i17 MyVector::MyVector(int size = 0)18 {19 m_space = new T[size];20 m_len = size;21 }22 //MyVector myv2 = myv1;23 template 24 MyVector::MyVector(const MyVector &obj)25 {26 //根据大小分配内存27 m_len = obj.m_len;28 m_space = new T[m_len];29 //copy数据30 for (int i = 0; i36 MyVector::~MyVector()37 {38 if (m_space != NULL)39 {40 delete[] m_space;41 m_space = NULL;42 m_len = 0;43 44 }45 }46 template 47 T& MyVector::operator [](int index)48 {49 return m_space[index];50 }51 template 52 MyVector & MyVector::operator=(const MyVector &obj)53 {54 //先把a2的内存释放掉55 if (m_space != NULL)56 {57 delete[] m_space;58 m_space = NULL;59 m_len = 0;60 61 }62 63 //根据a1分配内存64 m_len = obj.m_len;65 m_space = new T[m_len];66 67 //copy数据68 for (i = 0; i

声明:本文部分素材转载自互联网,如有侵权立即删除 。

© 版权声明
THE END
喜欢就支持一下吧
点赞0赞赏 分享
相关推荐
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容