C++之泛型编程(模板)
来源:互联网 发布:手机淘宝发不出去图片 编辑:程序博客网 时间:2024/06/06 14:13
1.模板综述
背景
- 有时候许多函数或子程序的逻辑结构是一样的,只是要处理的数据类型不一样
- 有时候多个类具有相同逻辑的成员函数和成员变量,只是成员变量的数据类型以及成员函数的参数类型不一样
- 模板就是解决数据类型不一致造成代码冗余的一种机制,本质上就是数据类型参数化,用一种逻辑结构抽象出多种数据类型对应的函数或者类
2.函数模板
2.1基础语法
示例代码
#include <iostream>using namespace std;template <typename T>//模板说明T myFunc(T size)//函数实现{ cout << "size:" <<size<< endl; return size;}int main(void){ myFunc(8);//自动推导调用 myFunc<float>((float)12.9);//显示调用 cout << "Hello!" << endl; //system("pause"); return 0;}
- 模板说明里面的类属参数在函数定义里面一定要使用,普通类型可以不使用
- 可以使用多个类型参数进行模板说明
#include <iostream>using namespace std;template <typename T1, typename T2>//模板说明,两个类型参数T2 mySort(T1 * array,T2 length){ T2 i = 0, j = 0; T1 tmp = array[0]; for (i = 0; i < length; i++) { for (j = i + 1; j < length; j++) { if (array[i] < array[j]) { tmp = array[i]; array[i] = array[j]; array[j] = tmp; } } } return i;}template <typename T1,typename T2>//模板说明T2 printArray(T1 *array,T2 length)//函数实现{ T2 i = 0; for (i = 0; i < length; i++) { cout << (int)array[i]<<""<< endl; } cout << endl; return i;}int main(void){ char myArray[10] = {1,3,87,54,98,37,33,63,89,2}; int size = sizeof(myArray) / (*myArray); mySort(myArray, size); printArray(myArray, size); cout << "Hello!" << endl; //system("pause"); return 0;}
2.2函数模板遇上函数重载
结论:
函数模板不允许自动类型转化
普通函数能够进行自动类型转换
调用规则
1 函数模板可以像普通函数一样被重载
2 C++编译器优先考虑普通函数
3 如果函数模板可以产生一个更好的匹配,那么选择模板
4 可以通过空模板实参列表的语法限定编译器只通过模板匹配
/*函数模板不允许自动类型转化普通函数能够进行自动类型转换*//* 1 函数模板可以像普通函数一样被重载 2 C++编译器优先考虑普通函数 3 如果函数模板可以产生一个更好的匹配,那么选择模板 4 可以通过空模板实参列表的语法限定编译器只通过模板匹配*/#include "iostream"using namespace std;int Max(int a, int b){ cout<<"int Max(int a, int b)"<<endl; return a > b ? a : b;}template<typename T>T Max(T a, T b){ cout<<"T Max(T a, T b)"<<endl; return a > b ? a : b;}template<typename T>T Max(T a, T b, T c){ cout<<"T Max(T a, T b, T c)"<<endl; return Max(Max(a, b), c);}void main(){ int a = 1; int b = 2; cout<<Max(a, b)<<endl; //当函数模板和普通函数都符合调用时,优先选择普通函数 cout<<Max<>(a, b)<<endl; //若显示使用函数模板,则使用<> 类型列表 cout<<Max(3.0, 4.0)<<endl; //如果 函数模板产生更好的匹配 使用函数模板 cout<<Max(5.0, 6.0, 7.0)<<endl; //重载 cout<<Max('a', 100)<<endl; //调用普通函数 可以隐式类型转换 system("pause"); return ;}
2.3函数模板实现机制
编译器介绍
- gcc(GNU C Compiler)编译器的作者是RichardStallman,也是GNU项目的奠基者。
- 什么是gcc:gcc是GNU Compiler Collection的缩写。最初是作为C语言的编译器(GNU C Compiler),现在已经支持多种语言了,如C、C++、Java、Pascal、Ada、COBOL语言等。
- gcc支持多种硬件平台,甚至对Don Knuth 设计的 MMIX 这类不常见的计算机都提供了完善的支持
gcc主要特征
1)gcc是一个可移植的编译器,支持多种硬件平台
2)gcc不仅仅是个本地编译器,它还能跨平台交叉编译。
3)gcc有多种语言前端,用于解析不同的语言。
4)gcc是按模块化设计的,可以加入新语言和新CPU架构的支持
5)gcc是自由软件
gcc编译过程
- 预处理(Pre-Processing)
- 编译(Compiling)
- 汇编(Assembling)
- 链接(Linking)
Gcc *.c –o 1exe (总的编译步骤)Gcc –E 1.c –o 1.i //宏定义 宏展开Gcc –S 1.i –o 1.s Gcc –c 1.s –o 1.o Gcc 1.o –o 1exe
结论:gcc编译工具是一个工具链。。。。
GCC常用编译选项
GCC常用编译步骤
1.gcc -E hello.c -o hello.i(预处理)
2.gcc -S hello.i -o hello.s(编译)
3.gcc -c hello.s -o hello.o(汇编)
4.gcc hello.o -o hello(链接)
以上四个步骤,可合成一个步骤
gcc hello.c -o hello(直接编译链接成可执行目标文件)
gcc -c hello.c或gcc -c hello.c -o hello.o(编译生成可重定位目标文件)
Gcc编译多个文件
hello_1.hhello_1.cmain.c
一次性编译
gcc hello_1.c main.c –o newhello
独立编译
gcc -Wall -c main.c -o main.ogcc -Wall -c hello_1.c -o hello_fn.ogcc -Wall main.o hello_1.o -o newhello
反汇编观察
- 编译器并不是把函数模板处理成能够处理任意类的函数
- 编译器从函数模板通过具体类型产生不同的函数
- 编译器会对函数模板进行两次编译
- 在声明的地方对模板代码本身进行编译;
- 在调用的地方对参数替换后的代码进行编译。
3.类模板
类模板与函数模板的定义和使用类似,我们已经进行了介绍。 有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,如下面语句声明了一个类:
类模板用于实现类所需数据的类型参数化
类模板在表示如数组、表、图等数据结构显得特别重要,
这些数据结构的表示和算法不受所包含的元素类型的影响
使用类模板声明对象的时候要显示指定形式参数的具体类型,以便C++编译器给对象分配具体的内存
3.1普通类模板的语法
单个类的语法
#include <iostream>using namespace std;template <typename T>class A{public: A(int a = 0) { this->a = a; } void printA() { cout << a << endl; }protected:private: T a;};int main(void){ A<int> a1; a1.printA(); A<int> a2(19); a2.printA(); cout<<"Hello!"<<endl; return 0;}
模板类作函数参数
#include <iostream>using namespace std;template <typename T>class A{public: A(int a = 0) { this->a = a; } void printA() { cout << a << endl; }protected:private: T a;};//类模板 做函数参数//参数 ,C++编译器 要求具体的类 所以所 要 A<int> &a void UseA(A<int> &a){ a.printA();}int main(void){ A<int> a1; UseA(a1); A<int> a2(19); UseA(a2); cout<<"Hello!"<<endl; return 0;}
3.2继承中的类模板语法
模板类派生时, 需要具体化模板类. C++编译器需要知道 父类的数据类型具体是什么样子的。要知道父类所占的内存大小是多少。只有数据类型固定下来,才知道如何分配内存。
模板类派生时, 需要具体化模板类. C++编译器需要知道 父类的数据类型具体是什么样子的。要知道父类所占的内存大小是多少。只有数据类型固定下来,才知道如何分配内存。
从模板类派生普通类
#include <iostream>using namespace std;template <typename T>class A{public: A(int a = 0) { this->a = a; } void printA() { cout << a << endl; }protected: T a;private:};class B:public A<int>{public: B(int a = 10, int b = 20) : A<int>(a) { this->b = b; } void printB() { cout << "a:" << a << " b: " << b << endl; }protected:private: int b;};int main(void){ B b1(1, 2); b1.printB(); cout<<"Hello!"<<endl; return 0;}
从模板类派生模板类
#include <iostream>using namespace std;template <typename T>class A{public: A(int a = 0) { this->a = a; } void printA() { cout << a << endl; }protected: T a;private:};template <typename T>class C :public A<T>{public: C(T a, T c) :A<T>(a) { this->c = c; } void printC() { cout << "a:" << a << "c:" << c << endl; }private: T c;protected:};int main(void){ C<int> c1(1, 2); c1.printC(); cout<<"Hello!"<<endl; return 0;}
3.2类模板知识体系梳理
类模板函数全部写在类的内部
#include <iostream>using namespace std;template <typename T>class Complex{ friend Complex MySub(Complex c1, Complex c2) { Complex tmp(c1.a-c2.a,c1.b-c2.b); return tmp; } friend ostream & operator<<(ostream & out, Complex & c) { out << c.a << " + " << c.b << "i" << endl; return out; }public: Complex(T a, T b) { this->a = a; this->b = b; } Complex operator+(Complex & c2) { Complex tmp(a + c2.a,b + c2.b); return tmp; } void printCom() { cout << a << " + " << b << "i" << endl; }protected:private: T a; T b;};int main(void){ //需要把模板类 进行具体化以后 才能定义对象 C++编译器要分配内存 Complex<int> c1(1, 2); Complex<int> c2(3, 4); Complex<int> c3 = c1 + c2; //c3.printCom(); cout << c3 << endl; //滥用友元函数 { Complex<int> c4 = MySub(c1, c2); cout << c4 << endl; } cout<<"Hello!"<<endl; return 0;}
类模板函数全写在类的外部,但在同一个cpp中
如果一个模板类具有友元函数,且该友元函数的形参包含模板类对象,则需要进行类模板和友元函数的前置声明。
需要注意的是:
- 模板类和模板类里面的友元函数需要进行前置声明
- 友元函数在前置声明的时候,函数名后面不指定具体的数据类型,但是在模板类里面声明的时候,需要在函数名后面紧跟具体的数据类型,比如
<T>
- 模板类的成员函数在类的外面实现的时候,需要注意参数列表,函数名(类作用域)以及返回类型是否需要强行指定具体的数据类型,以便编译器确定分配内存
- 友元函数在类的外部实现的时候,不需要管函数名前面的类作用域以及函数名后面的具体数据类型,只需要注意参数列表以及返回值是否需要指定数据类型即可
- 所有函数在类的外部实现的时候,其函数名后面都不用管具体的数据类型。
- 友元函数调用的时候,需要在函数名后面紧跟具体数据类型。言外之意,前置声明和外部实现的格式一样,类的内部声明和调用的时候,函数名后面都要具体的数据类型。
#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>ostream& operator<<(ostream& out, Complex<T>& c);//友元函数前置声明时不需要在函数名后面指定具体数据类型template <typename T>class Complex{ friend Complex<T> MySub<T>(Complex<T>& c1, Complex<T>& c2); //在模板类里面声明友元函数的时候,需要在函数名后面紧跟具体的数据类型,比如<T> friend ostream & operator<<<T>(ostream & out, Complex<T> & c);//在模板类里面声明友元函数的时候,需要在函数名后面紧跟具体的数据类型,比如<T> /* friend ostream & operator<<(ostream & out, Complex<T> & c); 如果operator<<后面没有<T>则会报错,所以模板类里面存在友元函数的时候, 除了进行友元函数的前置声明意外,还需要在模板类里面进行函数名数据类型具体化, 即指定具体的typename参数类型紧跟在函数名后面。 */public: Complex(T a, T b); Complex operator+(Complex & c2); void printCom();protected:private: T a; T b;};/*模板类的成员函数在类的外面实现的时候,需要注意参数列表,函数名(类作用域)以及返回类型是否需要强行指定具体的数据类型,以便编译器确定分配内存*/template <typename T>Complex<T>::Complex(T a, T b){ this->a = a; this->b = b;}template <typename T>Complex<T> Complex<T>::operator+(Complex<T> & c2){ Complex<T> tmp(a + c2.a, b + c2.b); return tmp;}template <typename T>void Complex<T>::printCom(){ cout << a << " + " << b << "i" << endl;}/*友元函数在类的外部实现的时候,不需要管函数名前面的类作用域以及函数名后面的具体数据类型,只需要注意参数列表以及返回值是否需要指定数据类型即可*/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>ostream & operator<<(ostream & out, Complex<T> & c){ out << c.a << " + " << c.b << "i" << endl; return out;}int main(void){ //需要把模板类 进行具体化以后 才能定义对象 C++编译器要分配内存 Complex<int> c1(1, 2); Complex<int> c2(3, 4); Complex<int> c3 = c1 + c2; //c3.printCom(); cout << c3 << endl; //滥用友元函数 { Complex<int> c4 = MySub<int>(c1, c2);//友元函数调用的时候,需要在函数名后面紧跟具体数据类型 cout << c4 << endl; } cout<<"Hello!"<<endl; return 0;}
结论:不要滥用友元函数,一般友元函数只适用于重载<<或者>>操作符。
类模板函数全写在类的外部,但在不同的.h和cpp中
由于模板的实现机制在本质上是两次编译,所以如果只在主程序里面包含头文件(类模板的声明),编译器不会自动寻找cpp文件里面的成员函数和友元函数的函数体。所以会出现找不到某个函数体的错误。只能是包含实现函数体的cpp文件,而cpp文件又包含了h文件,所以实质上是包含了类模板的声明以及类模板函数的实现,故业界都是将这两部分(.h和.cpp)写在同一个文件中,叫做hpp文件,只需要在提供的开源库里面包含该hpp文件,即可使用类模板。
- 传统类的头文件(类模板声明部分)
#pragma once#include <iostream>using namespace std;template <typename T>class Complex{ friend ostream & operator<< <T> (ostream &out, Complex &c3);public: Complex(T a, T b); void printCom(); Complex operator+ (Complex &c2);private: T a; T b;};
- 传统类的实现部分(具体类模板函数的实现部分)
#include <iostream>using namespace std;#include "complex.h"//构造函数的实现 写在了类的外部template <typename T>Complex<T>::Complex(T a, T b){ this->a = a; this->b = b;}template <typename T>void Complex<T>::printCom(){ cout << "a:" << a << " b: " << b << endl;}template <typename T>Complex<T> Complex<T>::operator+ (Complex<T> &c2){ Complex tmp(a + c2.a, b + c2.b); return tmp;}template <typename T>ostream & operator<<(ostream &out, Complex<T> &c3){ out << c3.a << " + " << c3.b << "i" << endl; return out;}
- 测试程序(包含hpp文件)
#include <iostream>using namespace std;#include "complex.cpp"void main(){ //需要把模板类 进行具体化以后 才能定义对象 C++编译器要分配内存 Complex<int> c1(1, 2); Complex<int> c2(3, 4); Complex<int> c3 = c1 + c2; cout << c3 << endl; cout << "hello..." << endl; return;}
3.3类模板中的static关键字
- 类模板—>实例化—>模板类
每一个模板类有自己的类模板数据成员,该模板类的所有对象共享一个static数据成员 - 和非模板类的static数据成员一样,模板类的static数据成员也应该在文件范围定义和初始化
- 每个模板类有自己的类模板的static数据成员副本
/*编译器并不是把函数模板处理成能够处理任意类的函数 编译器从函数模板通过具体类型产生不同的函数 编译器会对函数模板进行两次编译 在声明的地方对模板代码本身进行编译;在调用的地方对参数替换后的代码进行编译。*/#include <iostream>using namespace std;template <typename T>class AA{public: static T m_a;protected:private:};template <typename T>T AA<T>::m_a = 0;class AA1{public: static int m_a;protected:private:}; int AA1::m_a = 0;class AA2{public: static char m_a;protected:private:};char AA2::m_a = 0;void main(){ AA<int> a1, a2, a3; a1.m_a = 10; a2.m_a ++; a3.m_a ++; cout << AA<int>::m_a << endl; AA<char> b1, b2, b3; b1.m_a = 'a'; b2.m_a ++; b2.m_a ++ ; cout << AA<char>::m_a << endl; //m_a 应该是 每一种类型的类 使用自己的m_a cout<<"hello..."<<endl; system("pause"); return ;}
3.4类模板小结
类模板的声明
1.先写出一个实际的类。由于其语义明确,含义清楚,一般不会出错。
2.将此类中准备改变的类型名(如int要改变为float或char)改用一个自己指定的虚拟类型名(如上例中的numtype
)。
3.在类声明前面加入一行,格式为(class和typename作用一样): template <class
虚拟类型参数>
如:
template <class numtype> //注意本行末尾无分号 class Compare {…}; //类体
4.用类模板定义对象时用以下形式: 类模板名<实际类型名> 对象名;
类模板名<实际类型名> 对象名(实参表列);
如:
Compare<int> cmp; Compare<int> cmp(3,7);
5.如果在类模板外定义成员函数,应写成类模板形式: template <class 虚拟类型参数>
函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表列) {…}
6.类模板的类型参数可以有一个或多个,每个类型前面都必须加class
或者typename
,如:
template <class T1,class T2>class someclass{…};
在定义对象时分别代入实际的类型名,如: someclass<int,double> obj;
7.和使用类一样,使用类模板时要注意其作用域,只能在其有效作用域内用它定义对象。
8.模板可以有层次,一个类模板可以作为基类,派生出派生模板类。
4.模板在工程中的应用
综述
1.模板是C++中类型参数化的多态工具,提供函数模板和类模板
2.模板定义从模板说明开始,类属参数必须在模板实现中至少使用一次
3.同一个类属参数可以用于多类模板
4.类属参数可用于函数形参,返回类型以及声明函数中的变量
5.模板由编译器根据实际的数据类型实例化,生成实际的可执行代码,从而得到模板函数和模板类
6.函数模板可以进行重载
7.类模板可以进行派生继承
工程中用到的容器
所有容器提供的都是值(value)语意,而非引用(reference)语意。容器执行插入元素的操作时,内部实施拷贝动作。所以STL容器内存储的元素必须能够被拷贝(必须提供拷贝构造函数)。
案例
设计一个数组模板类( MyVector ),完成对int、char、Teacher类型元素的管理。
- 类模板定义
- 构造函数
- 拷贝构造函数
- 重载操作符<< [] =操作符
- 从数组模板中进行派生
使用基础数据类型以及一般的自定义类对象作为容器元素
- 容器/数组类模板头文件
#pragma once // 保证头文件只被编译一次。#include <iostream>using namespace std;template <typename T>class MyVector{public: MyVector(int size = 0);//构造函数 MyVector(const MyVector & obj);//拷贝构造函数 ~MyVector();//析构函数 T & operator[](int index); MyVector & operator=(MyVector obj); int getLen(); friend ostream & operator<< <T>(ostream & out, const MyVector<T> & obj);protected:private: int m_len; T *m_space;};
- 容器/数组类模板实现文件
#include "myvector.h"template <typename T>T & MyVector<T>::operator[](int index){ return m_space[index];}template <typename T>MyVector<T>::MyVector(int size = 0)//构造函数{ m_space = new T[size]; m_len = size;}template <typename T>MyVector<T>::MyVector(const MyVector & obj)//拷贝构造函数{ m_len = obj.m_len; m_space = new T[m_len]; 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_len = 0; m_space = NULL; }}template <typename T>MyVector<T> & MyVector<T>::operator=(MyVector<T> obj){ /*1.释放旧内存*/ if (m_space != NULL) { delete[]m_space; m_space = NULL; m_len = 0; } /*2.重新分配内存*/ m_space = new T[obj.m_len]; m_len = obj.m_len; /*3.拷贝数据*/ for (int i = 0; i < m_len; i++) { m_space[i] = obj[i]; } /*4.返回左值本身*/ return *this;}template <typename T>int MyVector<T>::getLen(){ return m_len;}template <typename T>ostream & operator<<(ostream & out, const MyVector<T> & obj){ for (int i = 0; i < obj.m_len; i++) { cout << obj.m_space[i] << " "; } cout << endl; return out;}
- 容器/数组类测试文件
#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include "myvector.cpp"//注意包含的是cpp文件using namespace std;class Teacher{public: Teacher() { age = 22; strcpy(name, "Goopher"); } Teacher(int age, char *name) { this->age = age; strcpy(this->name,name); } void printTeacher() { cout << name << "," << age << endl; }protected:private: int age; char name[32];};class Test1{public:protected:private: int a;};class Test2{public:protected:private: int a; static int b;//static成员存储在全局区,不占类对象的内存模型};class Test3{public: virtual void hello()//虚函数使得类对象具有VPTR指针,多占用一个指针的大小 { }protected:private: int a; static int b;};class Test4{public: virtual void hello() { } virtual void hello01() = 0;//多个虚函数只对应一个VPTR指针(一个虚函数表)protected:private: int a; static int b;};class Test5{public: virtual void hello() { } virtual void hello01() = 0; void printTest()//普通成员函数也不占据类对象的内存模型 { }protected:private: int a; static int b;};class Test6{public: virtual void hello() { } //virtual void hello01() = 0; void printTest() { } void printTest01()//多个普通成员函数也不占据类对象的内存模型 { }protected:private: int a; static int b;};int main(void){ /*初始化v1容器中的没一个对象并在初始化的时候逐个打印*/ MyVector<int> v1(10); for (int i = 0; i < v1.getLen(); i++) { v1[i] = i + 1; cout << v1[i] << " "; } cout << endl; /*初始化v2容器中的没一个对象并在初始化以后使用重载<<的方式打印*/ MyVector<int> v2 = v1; for (int i = 0; i < v2.getLen(); i++) { v2[i] = i*2 + 1; } cout <<v2<< endl; /*使用类对象设置容器并打印*/ Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4"); MyVector<Teacher> v3(4); v3[0] = t1; v3[1] = t2; v3[2] = t3; v3[3] = t4; for (int i = 0; i < v3.getLen(); i++) { Teacher tmp = v3[i]; tmp.printTeacher(); } cout<<"Hello!"<<endl; cout << sizeof(Test1)<<endl; cout << sizeof(Test2) << endl; cout << sizeof(Test3) << endl; cout << sizeof(Test4) << endl; cout << sizeof(Test5) << endl; cout << sizeof(Test6) << endl; Test6 t6; cout << sizeof(t6) << endl; system("pause"); return 0;}
优化后的Teacher类对象作为容器存储元素(类模板头文件和实现文件不变)
#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include "myvector.cpp"//注意包含的是cpp文件using namespace std;//1 优化Teacher类, 属性变成 char *panme, 购置函数里面 分配内存//2 优化Teacher类,析构函数 释放panme指向的内存空间//3 优化Teacher类,避免浅拷贝 重载= 重写拷贝构造函数 //4 优化Teacher类,在Teacher增加 << //5 在模板数组类中,存int char Teacher Teacher*(指针类型)//=====>stl 容器的概念 class Teacher{public: Teacher() { age = 22; name = new char[1]; strcpy(name, ""); } Teacher(int age, char *name) { this->age = age; this->name = new char[strlen(name) + 1]; strcpy(this->name,name); } Teacher(const Teacher &obj) { age = obj.age; name = new char[strlen(obj.name) + 1]; strcpy(name,obj.name); } ~Teacher() { if (name != NULL) { delete[]name; name = NULL; age = 22; } } void printTeacher() { cout << name << "," << age << endl; } friend ostream & operator<<(ostream & out, Teacher &obj); Teacher & operator=(const Teacher & obj) { if (name != NULL) { delete[]name; name = NULL; age = 22; } name = new char[strlen(obj.name) + 1]; strcpy(name, obj.name); age = obj.age; return *this; }protected:private: int age; char *name;};ostream & operator<<(ostream & out, Teacher &obj){ out << obj.name << "," << obj.age << endl; return out;}int main(void){ /*使用类对象设置容器并打印*/ Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4"); MyVector<Teacher> v3(4); v3[0] = t1; v3[1] = t2; v3[2] = t3; v3[3] = t4; for (int i = 0; i < v3.getLen(); i++) { Teacher tmp = v3[i]; tmp.printTeacher(); } cout<<"Hello!"<<endl; system("pause"); return 0;}
指针作为容器元素存储
#define _CRT_SECURE_NO_WARNINGS#include <iostream>#include "myvector.cpp"//注意包含的是cpp文件using namespace std;//1 优化Teacher类, 属性变成 char *panme, 购置函数里面 分配内存//2 优化Teacher类,析构函数 释放panme指向的内存空间//3 优化Teacher类,避免浅拷贝 重载= 重写拷贝构造函数 //4 优化Teacher类,在Teacher增加 << //5 在模板数组类中,存int char Teacher Teacher*(指针类型)//=====>stl 容器的概念 class Teacher{public: Teacher() { age = 22; name = new char[1]; strcpy(name, ""); } Teacher(int age, char *name) { this->age = age; this->name = new char[strlen(name) + 1]; strcpy(this->name,name); } Teacher(const Teacher &obj) { age = obj.age; name = new char[strlen(obj.name) + 1]; strcpy(name,obj.name); } ~Teacher() { if (name != NULL) { delete[]name; name = NULL; age = 22; } } void printTeacher() { cout << name << "," << age << endl; } friend ostream & operator<<(ostream & out, Teacher &obj); Teacher & operator=(const Teacher & obj) { if (name != NULL) { delete[]name; name = NULL; age = 22; } name = new char[strlen(obj.name) + 1]; strcpy(name, obj.name); age = obj.age; return *this; }protected:private: int age; char *name;};ostream & operator<<(ostream & out, Teacher &obj){ out << obj.name << "," << obj.age << endl; return out;}int main(void){ /*使用类对象设置容器并打印*/ Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4"); MyVector<Teacher*> v3(4); v3[0] = &t1; v3[1] = &t2; v3[2] = &t3; v3[3] = &t4; for (int i = 0; i < v3.getLen(); i++) { Teacher *tmp = v3[i]; tmp->printTeacher(); } cout << t1; cout<<"Hello!"<<endl; system("pause"); return 0;}
总结:类模板实现了数据结构(具体数据类型)和算法的分离,真正的实现了泛型编程。
- C++之泛型编程(模板)
- 泛型编程之模板
- 从汇编的眼光看C++(之递归函数与模板类) ,(之泛型编程) .
- Effective C++(七)模板与泛型编程
- Effective C++(七)模板与泛型编程
- [C++] 模板与泛型编程(1)
- [C++] 模板与泛型编程(2)
- [C++]模板与泛型编程(3)
- C++Primer---模板与泛型编程(一)
- C++(7)模板与泛型编程
- C++(7)模板与泛型编程
- Pure C++: 泛型编程,模板特殊化
- C++:模板与泛型编程
- [C++]模板与泛型编程
- 【C++】模板与泛型编程
- 【C/C++学院】(11)泛型编程/函数模板/类模板
- C++primer 阅读笔记-模板与泛型编程(函数模板)
- C++primer 阅读笔记-模板与泛型编程(类模板)
- git版本控制工具
- Vue同一个dom元素如何绑定多个点击事件?
- 微信小程序 加载 HTML 标签
- cefclient改入qt
- js实现几秒钟后页面自动跳转到指定页
- C++之泛型编程(模板)
- Log4j2 使用 ELK 简单介绍
- Codeforces Round #436 (Div. 2) D. Make a Permutation! 思维+贪心
- VirtualBox 不能创建64位虚拟机及启动错误的原因与解决方法
- 【MySQL笔记】SQL基本语法
- 袖珍世界
- 虚拟机配置静态 IP 以后无法连接的解决办法
- 1.项目简介
- 带返回值的存储过程和游标循环