• 【C++模板编程入门】模板介绍、模板定义、函数模板、类模板、模板的继承


    1、模块的引入

    1.1、示例代码

    #include 
    #include 
    
    using namespace std;
    
    //用template声明T是模板类
    template <typename T>
    T add(T a, T b)
    {
    	return a + b;
    }
    
    int main(void)
    {
    	//给add函数传入int类型,此时T就会被替换成int类型:int add(int a, int b)
    	int var1 = add(11, 22);
    	
    	//给add函数传入double 类型,此时T就会被替换成double类型:double add(double a, double b)
    	double var2 = add(11.11, 22.22);
    
    	cout << "var1=" << var1 << ", var2=" << var2 << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    1.2、代码分析

    root@ubuntu:# ./app 
    var1=33, var2=33.33
    
    • 1
    • 2

    (1)用template声明T是模板类型,也就是表示T是泛指数据类型,不是具体的数据类型;
    (2)add的参数用T来表示,说明add函数的传参和返回值都是模板类型,需要在调用add函数时来指定;
    (3)用模板来定义add函数使得代码更简洁,如果不使用模板则需要定义多个不同传参类型的add函数来实现函数重载,以适配不同类型的变量;

    2、模板的优劣势

    (1)优点:使用模板可以写出更加抽象和简洁的代码;可以将运算逻辑相同只有数据类型不同的函数用模板进行抽象,一个模板函数实现多个重载函数的效果,比如上面的add函数;
    (2)缺点:模板并不是必须,不使用模板一样可以实现同样的效果,使用模板需要额外学习和掌握模板编程的规则;
    总结:模板编程是用复杂度换取劳动量,用模板写代码抽象度更高,代码简洁,但是对程序员的要求也更高;

    3、模板的原理分析

    (1)模板编程可以写出类型无关的函数和类,代码更抽象,只关注逻辑部分,编译器在编译模板类/函数时不会指定具体的数据类型;
    (2)在使用模板类/函数时,需要指定模板参数的类型,编译器会替换模板参数为具体的类;
    (3)在最终编译成的代码中,是没有模板类/函数的,因为编译器已经在用到模板函数/类的地方都替换成了具体的函数类型;
    (4)模板是编译器提供的机制,实现了编译时的多态;

    4、模板实现运算符重载函数

    4.1、示例代码

    #include 
    #include 
    
    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 += other.age;
    	
    	return *this;
    }
    
    template <typename T> void People<T>::print(void)
    {
    	cout << "age = " << this->age << endl;
    }
    
    int main(void)
    {
    	//在定义People类的对象时需要指定模板类的具体类型
    	People<string> a("hello ");
    	People<string> b("word !");
    	People<string> c("");
    
    	a += b;
    	a.print();
    	
    	//在定义People类的对象时需要指定模板类的具体类型
    	People<int> d(4);
    	People<int> e(6);
    	People<int> f(0);
    
    	f = d + e;
    	f.print();
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    4.2、代码分析

    [root#]$ ./app 
    age = hello word !
    age = 10
    
    • 1
    • 2
    • 3

    (1)用友元函数对People类的"+、+="运算符进行重载,并且重载函数也是带模板类的;
    (2)在定义People类的对象时,需要指定模板类T的具体类型,这个类型也会传给运算符重载函数,这样类和重载函数的模板类T都被具体的数据类型所代替;
    (3)友元函数参考博客:https://blog.csdn.net/weixin_42031299/article/details/127699941;
    (4)运算符重载参考博客:https://blog.csdn.net/weixin_42031299/article/details/127593164;

    5、模板类的继承

    5.1、示例代码

    #include 
    #include 
    
    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
    //把T1和T2模板参数传给父类People,去初始化父类的模板参数
    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 x2 x1
    	//T1传给People的U2,T2传给People的T1
    	Man(T1 a1, T2 a2, T1 b1, T2 b2):People<T2,T1>(b2, b1),y1(a1),y2(a2){};
    
    };
    
    int main(void)
    {
    	//此时Man类的T1对应int,T2对应double
    	Man<int,double> m1(4, 5.5, 6, 7.7);		// y1=4, y2=5.5 	x1=7.7, x2=6
    	cout << "x1 = " << m1.x1 << ", x2 = " << m1.x2 << endl;
    	cout << "y1 = " << m1.y1 << ", y2 = " << m1.y2 << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    5.2、代码分析

    [root#]$ ./app 
    x1 = 7.7, x2 = 6
    y1 = 4, y2 = 5.5
    
    • 1
    • 2
    • 3

    (1)首先需要知道在定义模板类时模板参数是未知的,但是在用模板类实例化对象时需要传入模板类的类型,所以模板类都会被具体的类型所替代;
    (2)模板类的继承和一般类的继承规则是一样的,区别是继承父类时,父类中模板参数也会继承下来,子类在继承时需要给父类的模板参数指定具体类型,否则父类中的模板参数就还是未知,这样是会报错;

    6、非类型模板参数

    6.1、示例代码

    #include 
    #include 
    
    using namespace std;
    
    //非类型模板参数类
    //T是类型模板,代表一个未知数据类型
    //MAX是整数类型但是值未知,需要在定义模板类的对象时指定
    template <typename T, int MAX>
    class People
    {
    public:
    	char buffer[MAX];
    
    	T info;
    
    	People(){};
    	People(T a)
    	{
    		info = a;
    	}
    
    	void print(void)
    	{
    		cout << "" << info <<endl;
    		cout << "sizeof(buffer) = " << sizeof(buffer) << endl;
    	}
    };
    
    //非类型模板参数函数
    //T是类型模板,代表一个未知数据类型
    //VAL是整数类型但是值未知,需要在使用模板函数时指定
    template <typename T, int VAL> 
    T addValue (T const& x) 
    { 
        return x + VAL; 
    }
    
    int main(void)
    {
    	People<string, 77> p("hello word!"); 
    	p.print();
    
    	int a = 11;
    	cout << "addValue(a) = " << addValue<int, 22>(a) << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    6.2、代码分析

    [root#]$ ./app 
    hello word!
    sizeof(buffer) = 77
    addValue<int, 22>(a) = 33
    
    • 1
    • 2
    • 3
    • 4

    (1)非类型模板参数类/函数,顾明思义,模板参数代表的不是数据类型而是某个值;
    (2)类型模板参数在示例化时指定数据类型,非类型模板参数在示例化时指定具体的值,思路都是一样的;
    (3)非类型模板参数型限制:可以是常整数(包括enum枚举类型)或者指向外部链接对象的指针,但是浮点数和类对象(class-type)不允许作为非类型模板参数:

  • 相关阅读:
    QT scrollArea控件背景透明,但不影响子控件的方法
    CDN加速怎么实现缓存Range请求
    记一次利用工具升级 redhat 7.5到bclinux8.2操作
    贪心算法专题小结——区间相关问题
    RPC、算法题等知识学习总结
    Sychronized和ReentrantLock锁 面试题
    【MCS-51】中断系统原理及应用
    注解详解系列 - @EnableAspectJAutoProxy:启用AspectJ自动代理
    汽车EDI:德国大众 EDI 项目案例
    winRAR常用命令
  • 原文地址:https://blog.csdn.net/weixin_42031299/article/details/127814066