第14章-C++中的代码重用

explicit关闭单参数构造函数引起的隐式转换。
私有继承对应于公有继承:
公有继承,基类的公有方法成为子类的公有方法;
私有继承,基类的公有方法成为子类的私有方法。

14.4模板类
直接上代码,写了个非常简单的数组类,具有三个功能,push添加,pop弹出,show显示内容。

//模板类,头文件
template <typename T> //开头这里用template <typename T>格式
class array_r
{
    T items[10];
    int top;
public:
    array_r();
    void push(const T& item);//所有涉及到具体类型的,用T代替
    void pop();
    void show();
};

//说一下模板类的函数定义,必须要与模板类声明写在一起,而且放在头文件中!不能放在单独文件中!!

template <typename T>//同样,每个函数头上都要有template <typename T>
array_r<T>::array_r() //并且在类标示后要添加`<t>`,因为只要是模板类,所有的使用方法就必须后方带<>,然后传入类型。声明,定义,调用都必须有。
{
    top = 0;
}

template <typename T>
void array_r<T>::push(const T& item)
{
    items[top++] = item;
}

template <typename T>
void array_r<T>::pop()
{
    top--;
}

template <typename T>
void array_r<T>::show()
{
    for (int i = 0; i < top; ++i)
    {
        std::cout<<items[i]<<"  ";
    }
}
//使用:
    array_r<int> int_array;
    int_array.push(2);
    int_array.push(1);
    int_array.push(1);
    int_array.push(9);
    int_array.push(8);
    int_array.push(5);
    int_array.show();

    cout<<std::endl;

    int_array.pop();
    int_array.show();

    array_r<double> double_array;
    double_array.push(2.1);
    double_array.push(1.1);
    double_array.push(1.1);
    double_array.push(9.1);
    double_array.push(8.1);
    double_array.push(5.1);
    double_array.show();

    cout<<std::endl;

    double_array.pop();
    double_array.show();

//输出结果:
2  1  1  9  8  5  
2  1  1  9  8  
2.1  1.1  1.1  9.1  8.1  5.1  
2.1  1.1  1.1  9.1  8.1  

没毛病,没瑕疵,很完美~int和double都适用起来了。
说三点:
1、模板类不能用分离编译,必须声明和定义一起放在头文件中
2、模板类类名后面无时无刻都要带上<T>来表征其是模板类。啥时候都不能丢(虽然在类声明中成员函数的返回类型时可以缩写,不用带,但是都写上好记还没啥坏处,等用6了再省略也不急。)
3、调用时候在<>中传入操作的类型就好了,在编译时会将模板中的T代替。
(总结一下貌似模板类也没那么复杂,当然也有可能是复杂的东西我还没碰到。。。)

非类型参数:
很简单,举个栗子:

template <typename T, int n>
class ArrayTP
{...}

尖括号中的int n就是非类型参数了(其实就是一个参数。。。),这种用法常见在定义数组的长度上。用的时候:ArrayTP <double, 12> an_array;这样就好了。是不是很熟悉?用eigen库的时候,定义数组是不是经常这样:Eigen::Matrix<double,3, 3>?定义一个3*3double矩阵~可以看看源码,后台很有可能就是这样定义的:

template <typename T, int m, int n>
class Matrix
{...}

关于非类型参数就说一点,就是每一个参数值都将生成自己的声明。
也就是说下面两句将生成两个独立的声明来创建实例:

ArrayTP<double, 12> eggweight;
ArrayTP<double, 13> appleweight;

多样性中有一点:
模板类的类型参数可以有默认值,例如:

template <typename T = int> //这样就提供了默认值
class array_r {...}

类比模板函数呢,模板函数声明中则不可以有默认值。但是都可以为非类型参数提供默认值。

实例化与具体化
同样,类似模板函数一样,模板类也存在实例化与具体化功能。

1、隐式实例化。
很简单,就是在创建对象时生成具体类定义。
array_r<int> int_array;
这句将生成具体的类定义,并创建对象。
2、显示实例化。
手动控制生成一个类定义,不要等到创建对象时再生成。
template class array_r<long>;
在这句手动生成long类型的模板类定义,而不是等到要创建对象时隐式创建。注意一点即可:声明必须位于模板定义所在的名称空间中。简单说就是这句必须与模板定义放在一起。
3、显示具体化。
同比于模板函数,显示具体化也是为了某些不适用模板的特殊类型准备的,告诉编译器,在遇到这种特殊类型时,不要用泛型模板去生成类定义了,要用我这里单独定义的这个去生成~

template<> class array_r<string> 
{
...
};

格式上就是前缀用template<>,然后传入特殊类型的名称就好了。

多说一嘴显式实例化和显式具体化:
显式实例化是依托泛型模板,在模板声明后接的一条语句,强制让模板生成个类定义。
显式具体化则是直接废掉泛型模板类声明(当然是在用特殊类型时),用我重新定义的模板声明,所以它并不是一条语句,而是重新定义泛型模板类的声明

后面还有部分具体化,在有两个模板类型的时候,只具体化一个:

//一般模板类:
template <typename T1, typename T2> 
class pair
{...}

//部分具体化
template <typename T1>
class pair<T1, int>
{...}

看三点:
1、template后面的参数是未被具体化的类型参数。
2、类名后面接上<>,在其中写上类型参数。这里是未被具体化的T1和被具体话的int。这点与一般模板类不一样,一般模板类啥都没有!
3、既然是具体化,那么就必须是一个完整的类声明。{…}写起来。

14.4.8将模板用作参数
前面说过,模板类中的类型参数typename T,还有非类型参数int n,还有一种常见的就是模板类型参数。说白了就是这个参数是个模板。
这个将模板用作参数有点小绕,想了一阵~~
先看栗子:

//模板类中的模板参数:
template <template <typename T> class Thing>
class Crab
{
private:
    Thing <int> s1;
    Thing <double> s2;
public:
    Crab(){};
    bool push(int a, double x) { return s1.push(a) && s2.push(x);}
    bool pop(int& a, double& x) { return s1.pop(a) && s2.pop(x);}
};

先看第一句:

template <template <typename T> class Thing>

这句就很绕,一点点解析:
首先最外层的template和其<>表征这是一个模板类。
然后外层尖括号中的内容template <typename T> class Thing表示:
1、这个类需要一个参数,
2、这个参数是个模板类(说白了还是需要类型参数,只是不在是基本类型了,而是模板类型),
3、模板类的模板格式需为template <typename T>这种格式。
4、我们暂且将这种模板格式的类称为Thing吧。

之后,出现了数据成员Thing <int> s1;这句的意思是,让我们来创建一个Thing格式(其实也就是template <typename T>这种格式)的模板类型对象s1吧,当然,既然是模板类型,实例化时需传入参数,OK,还是按照Thing格式传入,这里就传入了个<int>

最后的push和pop方法,是假设了我以后调用时,传入我的Thing格式的模板类,都定义了这两个函数,不然会报错。

纵观整个模板类定义的感觉就是:这里Thing的作用就是给template <typename T> class这种格式的模板类起了个统称名字,然后用的时候,这种格式的模板类就叫Thing了。比如有个Thing格式、参数为int、还是由模板定义的类型对象 s1。

再看看调用:

Crab<Stack> nebula;

给Crab传入了Stack,这里Stack必须是模板类、且格式为template <typename T>这种格式、且有push和pop方法。
这样呢,在定义s1的时候,就是这样了:Stack<int> s1;同理,s2就是这样:Stack<double> s2;整个过程就用Stack这个具体的模板类代替掉了Thing所表征的template <typename T> class此种格式的模板类群集。

很绕的感觉,不过细想也还行,模板参数就是让传入一个模板类,只要符合格式就OK。类型参数要求随便传入一种基本类型当做参数,模板参数要求随便传入一种符合格式的模板类当做参数。

当然还可以混合使用模板参数和常规参数:

template <template <typename T> class Thing, typename U, typename V>
class Crab
{...}

模板类和友元
模板类也可以有友元分为3类:
1、非模板友元。
2、约束模板友元,即友元的类型取决于类被实例化时的类型。
3、非约束模板友元,即友元的所有具体化都是类的每一个具体话的友元。
这块看起来也是个大坑,后续再总结,这里就先说下定义吧~在撸源代码的时候可能会遇到。

下面说个模板类救星,简直大救星!
模板别名typedef

typedef Eigen::Matrix<float2, 2> Matrix2f;
typedef Eigen::Matrix<float3, 3> Matrix3d;
typedef Eigen::Matrix<double2, 2> Matrix2d;
typedef Eigen::Matrix<double3, 3> Matrix2d;

//用的时候直接用后面的名字就好了,就不用前面一大大大大大串了
Matrix2f m = {1, 23, 4};//似不似很简单~~

当然,typedef之前就见过,也可以用于非模板,比如:

typedef const int* (*pa1)[10];//这个是不是比较绕?

总的来说就是:pa1是个指针,指向一个数组,此数组有10个元素,这是10个元素的类型是const int 型指针。
C11提供稍微先进一丢丢的using=方案:

using const int* (*)[10] = pa1;

为啥叫先进一丢丢呢?为啥不给我定义成这样???

define *pa1 -> [10*(const int*)]//是不是一眼就看明白了????嗯??!!行了,要啥自行车,要定义成这么一眼明了的,还怎么装逼。。。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值