C++泛编程(4)

1.类模板具体化

有了函数模板具体化的基础,学习类模板的具体化很简单。类模板具体化有两种方式,分别为部分具体化和完全具体化。假如有类模板:

template<class T1,class T2>
class AA 
{
    public:
        T1 m_a;
        T2 m_b;
        AA(T1 a,T2 b):m_a(a),m_b(b)
        {
            cout<<"类模板构造函数"<<endl;
        }
        void show();
};
template<class T1,class T2>
void AA<T1,T2>::show()
{
    cout<<m_a<<" "<<m_b<<endl;
}

这里把方法写在类外是为了更好地区分语法上的差别。这个模板的部分具体化和完全具体化有什么差别呢?

部分具体化

部分具体化是类模板特有的,它是指模板类的部分通用类型具有指定的数据类型,示例如下:

template<class T1>
class AA<T1,string>
{
    public:
        T1 m_a;
        string m_b;
        AA(T1 a,string b="ZhangSan"):m_a(a),m_b(b)
        {
            cout<<"类模板部分具体化构造函数"<<endl;
        }
        void show();
};
template<class T1>
void AA<T1,string>::show()
{
    cout<<m_a<<" "<<m_b<<endl;
}

这就是AA模板类的一个不完全具体化版本了。这个AA模板缺省了一个通用类型参数,在类外对方法进行定义时需要指出AA的两个模板参数,具体参数也不可以省略,其余不变。

完全具体化

完全具体化就是类模板完全没有通用参数,它才像是函数模板具体化,举例如下:

template<>
class AA<char,string>
{
    public:
        char m_a;
        string m_b;
        AA(char a='m',string b="ZhangSan"):m_a(a),m_b(b)
        {
            cout<<"类模板部分具体化构造函数"<<endl;
        }
        void show();
};
void AA<char,string>::show()
{
    cout<<m_a<<" "<<m_b<<endl;
}

这里有一个细节需要大家注意,我们在类外定义方法时不再需要写template标识符,但依然需要注明AA的两个具体的模板参数。
下面我们来调用一下模板类,看看调用的优先级:

int main()
{
    AA<int,int> aa(10,20);
    aa.show();
    AA<int,string> bb(10);
    bb.show();
    AA<char,string> cc('w',"LiSi");
    cc.show();
}

在这里插入图片描述
可以看到,如果满足完全具体化类的条件,会优先使用完全具体化类,如不满足,满足不完全具体化类条件优先使用不完全具体化类。只有当二者都不满足时才会使用普通的模板类。

2.类模板与继承

模板类的花样很多,因此类的继承玩法也很多。我们总结五种常用的用法,但其实只需知晓原理就行,用到的时候如果忘记了可以回来查看。

2.1模板类继承普通类

这个任务最简单,我们只需要把模板类当成普通类,写一个集成即可:

class AA
{
    public:
        int m_a;
        AA(int a):m_a(a)
        {cout<<"这里是AA的构造函数"<<endl;}
        void fun1()
        {cout<<"这里是fun1函数 "<<m_a<<endl;}
};
template<class T>
class BB:public AA
{
    public:
        T m_b;
        BB(T b,int a):AA(a),m_b(b)
        {cout<<"这里是BB的构造函数"<<endl;}
        void fun2()
        {cout<<"这里是fun2函数 "<<m_b<<endl;}
};

int main()
{
    BB<string> b("ZhangSan",10);
    b.fun1();
    b.fun2();
}
// 输出为:这里是AA的构造函数
//		  这里是BB的构造函数     
//		  这里是fun1函数 10      
//		  这里是fun2函数 ZhangSan

与普通的类继承一样,把基类的构造函数安排好就可以了。

2.2普通类继承模板类的实例化版本

实例化的类模板和普通类没有区别,继承方法也是一样。我们对上例中两个类稍作调整:

template<class T>
class BB
{
    public:
        T m_b;
        BB(T b):m_b(b)
        {cout<<"这里是BB的构造函数"<<endl;}
        void fun2()
        {cout<<"这里是fun2函数 "<<m_b<<endl;}
};
class AA:public BB<string>
{
    public:
        int m_a;
        AA(int a,string b):BB(b),m_a(a)
        {cout<<"这里是AA的构造函数"<<endl;}
        void fun1()
        {cout<<"这里是fun1函数 "<<m_a<<endl;}
};
int main()
{
    AA b(10,"ZhangSan");
    b.fun1();
    b.fun2();
}
// 输出为:这里是BB的构造函数
//		  这里是AA的构造函数     
//		  这里是fun1函数 10      
//		  这里是fun2函数 ZhangSan

继承一个实例化的模板类和继承普通类没有区别,但这种继承也并不实用。

2.3普通类继承类模板

继承模板类的一个具体化版本并不实用,我们更多用到的是继承模板类本身,以保留模板类的特性。但想做到这样,也得给子类加上模板头:

template<class T>
class BB
{
    public:
        T m_b;
        BB(T b):m_b(b)
        {cout<<"这里是BB的构造函数"<<endl;}
        void fun2()
        {cout<<"这里是fun2函数 "<<m_b<<endl;}
};
template<class T>
class AA:public BB<T>
{
    public:
        int m_a;
        AA(int a,T b):BB<T>(b),m_a(a)
        {cout<<"这里是AA的构造函数"<<endl;}
        void fun1()
        {cout<<"这里是fun1函数 "<<m_a<<endl;}
};
int main()
{
    AA<string> b(10,"ZhangSan");
    b.fun1();
    b.fun2();
}
// 输出为:这里是BB的构造函数
//		  这里是AA的构造函数     
//		  这里是fun1函数 10      
//		  这里是fun2函数 ZhangSan

AA现在也被迫成了模板类,所以我们实例化AA的时候需要指定模板通用类型。此外,我们在子类AA的构造函数中需要使用BB的构造函数,也应当注意语法的变化。这种继承也是比较常用的。

2.4模板类继承模板类

有了普通类继承模板类的基础,模板类继承模板类就非常好理解。回到上例,假如AA(子类)本身就是一个模板类,通用参数的名字是U,我们把基类的通用参数加到template里就可以了,其他的语法基本不需要改:

template<class T>
class BB
{
    public:
        T m_b;
        BB(T b):m_b(b)
        {cout<<"这里是BB的构造函数"<<endl;}
        void fun2()
        {cout<<"这里是fun2函数 "<<m_b<<endl;}
};
template<class U,class T>
class AA:public BB<T>
{
    public:
        U m_a;
        AA(U a,T b):BB<T>(b),m_a(a)
        {cout<<"这里是AA的构造函数"<<endl;}
        void fun1()
        {cout<<"这里是fun1函数 "<<m_a<<endl;}
};
int main()
{
    AA<int,string> b(10,"ZhangSan");
    b.fun1();
    b.fun2();
}
// 输出为:这里是BB的构造函数
//		  这里是AA的构造函数     
//		  这里是fun1函数 10      
//		  这里是fun2函数 ZhangSan

2.5模板类继承模板参数给出的类

这是一种很新奇的玩法,简单地说就是基类是不确定的,具体继承谁要在调用时才会确定,很像是容器嵌套:

class AA
{
    public:
        int m_a;
        AA(int a):m_a(a)
        {cout<<"这里是AA的构造函数"<<endl;}
        void fun1()
        {cout<<"这里是fun1函数 "<<m_a<<endl;}
};

template<class T>
class BB
{
    public:
        T m_b;
        BB(T b):m_b(b)
        {cout<<"这里是BB的构造函数"<<endl;}
        void fun2()
        {cout<<"这里是fun2函数 "<<m_b<<endl;}
};


template<class U,class T>
class CC:public U
{
    public:
        CC(T a):U(a)
        {cout<<"这里是CC的构造函数"<<endl;}
        void fun3()
        {cout<<"这里是fun3函数 "<<endl;}
};
int main()
{
    CC<AA,int> c1(15);
    c1.fun1();c1.fun3();
    cout<<endl;
    CC<BB<string>,string> c("LiSi");
    c1.fun1();c1.fun3();
}

这段代码会有些难度,大家可以自己写写试试。另外提示一点,template里定义的参数只有在当前的模板类起作用,后面的模板里即使有重名也不会影响代码。换句话说假如AA类用T1表示通用类型,CC类用T2,T3表示通用数据类型效果实际和这个例子上是一样的,不要被重名迷惑了。

这节我们具体学习了类模板的具体化和几种常用的继承,语法并不困难,我们也只需要理解原理,记不住的话后面用到的时候查一下就好。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值