STL库-类模板知识点总结

函数模板知识点看这篇文章:
函数模板知识点总结

1.类模板

1.1 类模板的声明

template<class 类型形参1, .....> class 类模板名{}

//举例
//CMath为类模板名
template<class A, class B>class CMath{
public:
    	A m_a;
    	B func(){...};
}

1.2 类模板的使用

使用类模板补习对类模板进行实例化,即,产生真正的类。类模板不能直接代表一个确定的类型,只有通过类型实参实例化成真正的类以后才具备真正类的功能。

  • 类模板被实例化的时候,类模板中的成员函数并没有实例化,成员函数只有在被调用的时候才会实例化。
  • 某些类型,虽然没有提供类模板所需要的全部功能,但是照样可以实例化模板,只要不调用那些没有提供功能的成员函数即可。
#ifndef CLASTMPL_H_INCLUDED
#define CLASTMPL_H_INCLUDED
#include <iostream>
#include <string>

using namespace std;

class Integer{
//自己定义了一个类,这个类只有类模板中的初始化功能,没有add()功能
public:
    Integer(int const& i):m_i(i){}
    int operator+(Integer const& other)const{
        return m_i + other.m_i;
    }
	//使用友元函数重载<<运算符,使得<<可以访问成员私有变量
    //友元函数的意识是,这个全局函数(操作符)是我的(这个类)的朋友
	//所以,可以访问我的私有成员变量
    friend ostream& operator<<(ostream& out, Integer const& that){
        out << that.m_i << endl;
        return out;
    }
private:
    int m_i;
};

//我们自己创建的类模板
template<class T>
class CMath{
public:
    CMath(T const& t1, T const& t2):m_t1(t1), m_t2(t2){}
    T add();
private:
    T m_t1;
    T m_t2;
};
//类模板成员在类模板外面去定义要点
//1.类模板的帽子不能丢了
//2.指定函数的作用域
template<class T>
T CMath<T>::add(){
    return m_t1 + m_t2;
}
//编译器帮我们生成的类
//class CMath<int>{...};
//在用类模板示例化类的时候,编译器生成的东西
/*
class CMat<>{
    public:
    private:
        int m_t1;
        int m_t2;
}
注意:开始生成的类中,只有成员变量,没有成员函数
       什么时候调用成员函数,成员函数什么时候出现在类中。
       比如调用了构造函数,构造函数就生成到类中。
       比如调用了add()函数,add()函数就生成到类中。
       所以说,一个用模板生成类是一个过程,不是一蹴而就的。
*/
void ClassTemplateTest_2(){
    int x = 10, y = 20;
    CMath<Integer> math1(10, 20);
    //虽然Integer类中没有定义+操作,
    //但是,由于我们没有使用add,所以,CMath类中不会生成add()
    //我们调用了构造函数,并且Integer中有构造函数,
    //所以,CMath类中生成了构造函数。
    cout << math1.add() << endl;
}
#endif // CLASTMPL_H_INCLUDED

1.3 类模板的静态成员

  • 类模板中的静态成员即不是每个对象拥有一份,也不是类模板拥有一份
  • 而应该是由类模板实例化出的每一个真正的类各有一份
  • 且为该实例化类定义的所有对象共享。
#ifndef STATIC_H_INCLUDED
#define STATIC_H_INCLUDED
#include <iostream>
#include <string>

using namespace std;

template<class T>class A{
public:
    static void print(){
        cout << "&m_i:" << &m_i << "---&m_t:" << &m_t << endl;
    }
    //类的静态变量声明在类里面
    //但是定义(赋值)要在类外面赋值
    static int m_i;
    static T m_t;

};
template<class T>int A<T>::m_i;
template<class T>T A<T>::m_t;//先不赋值,取随机数

void StaticTest(){
    cout << "-----使用A<int>对象和A<int>类调用A<int>类中的static变量-----"<<endl;
    A<int> x,y,z;
    //类的静态变量是一个类用一份
    //也就是说,用这个类定义的不同对象调用同一个静态变量
    x.print();
    y.print();
    z.print();
    A<int>::print();//用类直接调用函数
    cout << endl;

    cout << "-----使用A<double>对象和A<double>类调用A<double>中的static变量-----"<< endl;
    A<double> a,b,c;
    a.print();
    b.print();
    c.print();
    A<double>::print();
}

#endif // STATIC_H_INCLUDED

在这里插入图片描述

1.4 类模板的递归实例化

  • **前提:**我们可以使用任何类型来实例化类模板。
  • **定义:**由类模板实例化产生的类,也可以用来实例化类模板自身,这种作为即为类模板递归实例化
  • **作用:**通过这种方法可以构建空间上具有递归特性的数据结构(多维数组)。
#ifndef RECURSION_H_INCLUDED
#define RECURSION_H_INCLUDED
#include <iostream>
#include <string>
using namespace std;
template<class T>
class Array{
public:
    T& operator[](size_t i){
        return m_arr[i];
    }
private:
    T m_arr[10];
};


void RecursionTest(){
    //类模板的递归实例化
    //注意,尖括号的嵌套中间要加上空格
    Array<Array<int> > m;
    for(int i = 0; i <10; i++){
        for(int j=0; j<10;j++)
            m[i][j] = i +j;
    }
    for(int i = 0; i < 10; i++){
        for(int j = 0; j < 10; j++)
            std::cout << m[i][j] << ' ';
        cout << endl;
    }

#endif // 03_RECURSION_H_INCLUDED

在这里插入图片描述

1.5 类模板的全局特化(类模板的扩展)

1.5.1 全类特化和成员特化

为什么要特化?

我们定义了一个类模板,类模板里面的函数对于大部分的数据类型都适用,
但是,有极个别的类型不使用,这时候我们就要对这一两个特别的类型做特化。

特化是什么?

本质上,就是重写了针对某个特定数据类型的具体类。实际上就是写了一个类。
只不过,我们把他叫做类模板的特化。编译器在调用的时候,如果遇到了这个特化类,
即传入的参数符合特化类形式,也会优先调用特化类,而不会去依照着类模板再重新实例化一个类。

#ifndef SPECIAL_H_INCLUDED
#define SPECIAL_H_INCLUDED
#include <iostream>
#include <string>
#include <cstring>

using namespace std;

//我们自己创建的类模板
template<class T>
class CMath{
public:
    CMath(T const& t1, T const& t2):m_t1(t1), m_t2(t2){}
    T add();
private:
    T m_t1;
    T m_t2;
};

template<class T>
T CMath<T>::add(){
    return m_t1 + m_t2;
}

//成员特化:只对模板中的一个函数进行特化,这样比较省事
template<>char*const CMath<char*const>::add(){
    return strcat(m_t1, m_t2);
}


//全类特化--其实就是写了一个具体的类型
//template<>这个尖括号中没有内容,代表特化
template<>
class CMath<char* const>{
public:
    CMath<char* const>(char*const& t1, char*const& t2):m_t1(t1), m_t2(t2){}
    char* const add(){
        return strcat(m_t1, m_t2);
    }

private:
    char* const m_t1;
    char* const m_t2;
};



void SpecialTest(){
    char cx[256] = "hello", cy[256] = "world";
    CMath<char* const> m1(cx, cy);
    //两个字符串拼接实现
    cout << m1.add() << endl;
}

#endif // 04_SPECIAL_H_INCLUDED

1.6 局部特化

什么是局部特化呢?

将模板的参数给定一部分,啥意思呢?分为下面几种情况:

template<class T, class D>
class A{
	public:
    	static void foo(){
            cout<< "这是没有被特化的类" << endl;
        }
};

局部特化情况1:

给定部分类型形参。具体看下面例子。

template<class T> class A<T, short>{
	public:
    	static void foo(){
            cout << "这是指定定了一个类型形参为short的特化" << endl;
        }

};

局部特化情况2:

指定两个或多个类型形参一样,但是,不给定具体的类型形参。

template<class T>class A<T, T>{
	public:
    	static void foo(){
            cout << "指定两个类型形参为同一个类型,但是,没有给定具体值" <<endl;
        }
}

局部特化情况3:

指定两个或多个类型形参为指针形式。

template<class T, class D>class A<T*, D*>{
	public:
    	static void foo(){
            cout << "指定两个类型形参都为指针类型,没有给定具体的类型" <<endl;
        }
} 

其实,我们通过上面的几种情况发现一个规律,只要是特化的时候,没有把所有类型形参给出具体的类型值,比如指定这个类型形参为int,另一个类型形参为double,只要不是这样给明了的,都可以叫做局部特化。

同时,编译器在执行的时候,会按照传入的具体参数情况,选择一个匹配对最高的特化类进行实例化。
但是,特化多了,我们还有使用模板的必要了吗???
所以,尽量减少特化,无论是全局特化还是局部特化。

注意

类模板的局部特化,除非必要否则尽量不要特化,因此特化版本过多容易引发编译器匹配歧义。

#ifndef PARTIAL_H_INCLUDED
#define PARTIAL_H_INCLUDED
#include <iostream>

using namespace std;

template<class T, class D>
class CMath{
    public:
        static void foo(){
            cout << "原始版本1:CMath<T,D>::foo"<<endl;
        }
};

//局部特化
//只特化了一个类型参数,没有把所有的类型参数都特化
//这就是局部特化
template<class T> class CMath<T, short>{
    public:
        static void foo(){
            cout << "局部特化2:CMath<T, short>::foo" << endl;
        }
};

template<class T>class CMath<T,T>{
    public:
        static void foo(){
            cout << "3:CMath<T,T>:foo"  << endl;
        }

};

template<class T, class D>class CMath<T*, D*>{
    public:
        static void foo(){
            cout << "4:CMath<T*, D*>" << endl;
        }
};


void PartialTest(){
    CMath<int, double>::foo();//1

    CMath<int, short>::foo();//2
//    CMath<short, short>::foo();//匹配歧义
    CMath<int*,double*>::foo();//1
    //CMath<int*,int*>::foo();//匹配歧义
}
#endif // PARTIAL_H_INCLUDED

1.7 类型形参的缺省值

什么是缺省值?

其实就是默认值,我们可以给尖括号里的模板类型形参定义默认值,这样,如果我们如果我们的用模板生成实例化对象的时候,如果没有给定具体类型,就可以按照默认缺省值去生成。

#ifndef QS_H_INCLUDED
#define QS_H_INCLUDED
#include <iostream>
#include <string>
#include <typeinfo>

using namespace std;

//缺省值可以理解为默认值
//如果实例化类的时候,没有给定具体的类型,就按照缺省值实例化
//注意:如果第一个类型形参给了具体的类型,后面的类型形参也必须指定类型
template<class T=int, class D=int>
class A{
public:
    A(){}
    A(T t1, D d1):m_t(t1),m_d(d1){}
    void foo(){
        cout << "m_t type:" << typeid(m_t).name() <<'\n'<< "m_d type:" << typeid(m_d).name() << endl;
    }
private:
    T m_t;
    D m_d;
};


void DefaultTest(){
    A<double,double> a(10.1,20.2);//这里没有给定
    A<> a1;
    a.foo();
    a1.foo();

}

#endif // QS_H_INCLUDED

1.8 数值形的模板参数

什么是数值型模板参数?

就是定义类模板的时候,尖括号里可以写类型形参,也可以将普通数值类型作为参数

**注意:**只能将普通数值类型参数作为模板,别的都不行。

#ifndef VALPARAM_H_INCLUDED
#define VALPARAM_H_INCLUDED
#include <iostream>
#include <string>

using namespace std;

template<class T = double, size_t S = 15>
class Array{
public:
    T& operator[](size_t i){
        return m_arr[i];
    }
    size_t Size(){
        return S;
    }
private:
    T m_arr[S];
};


void ValparamTest(){
    Array<int> a;
    for(int i = 0; i < a.Size(); i++){
        a[i] = i + 1;
    }
    for(int i = 0; i < a.Size(); i++)
        std::cout << a[i] << ' ';
    std::cout <<std::endl;
}

#endif // VALPARAM_H_INCLUDED

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辛伯达岛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值