C++ 16章 模版与泛型编程

逃离我推掉我的手 2022-06-02 01:17 595阅读 0赞
  • 泛型编程:独立于任何特定类型来编写的代码,当使用泛型程序时候,我们提供类型或者值,程序实例就可以运行。模版是泛型编程的基础,就是创建类或者函数的蓝图或者说明公式。例如vector,我们提供类型,编译器在编译期间就会将其转换为对应的类或函数。

函数模版

  • 定义模版:例如比较不同类型的值,我们可以重载比较函数,但是这种方式是函数体相同,而参数类型不同而已。如果对每种希望比较的类型都不得不重复定义完全一样的函数体,非常繁琐且容易出错。这里我们就可以通过模版处理。
  • 函数模版:定义一个通用的模版,而不是为每一个类型都定义一个函数。模版就是公式,可以用来生成针对特定类型的函数版本。模版参数列表表示类或函数中用到的类型或值。T的实际类型,编译器根据使用传递的类型确定并生成对应的函数版本(编译器可以推断出模板参数类型)。

    template //模版参数列表,用逗号分割多个模版参数,且模板参数列表不可能空。
    //引用避免了参数赋值
    int compare(const T &v1 , const T &v2)
    {

    1. if(v1 < v2) return -1;
    2. if(v1 > v2) return 1;
    3. return 0;

    }

  • 实例化函数模版:调用模版,编译器通常根据函数实参来推断模版实参。编译器根据实参类型确定绑定到模版参数T的类型。compare(1,2);T为int。这个叫做模版的实例化,这些编译器生成的版本通常叫做模版实例。

  • 模板类型参数:就是typename T,T和内置类型一样,可以指定函数参数类型和返回值类型以及函数体内用作变量或类型转换声明。类型参数前必须加class或typename,以便编译器识别。template typename T只是告诉编译器T类型是一个模版,其具体类型根据调用者提供的类型替换。
  • 模版非类型参数:表示一个常量数值而非类型,通过特定类型名而非前面的关键字。例如比较两个字符串常量,由于不知道长度,因此可以定义两个非类型参数。编译器根据参数长度来代替非类型参数。编译器根据字面量大小代码u和v

    template //模版参数列表,用逗号分割多个模版参数
    int compare(const char (&v1)[u] , const char (&v2)[v])
    {

    1. return strcmp(p1,p2);

    }

  • 内联或常量函数模版:关键字放在模版参数列表之后,返回类型之前。

  • 模版编译:编译器碰到模版定义,不生成代码。只有实例特定版本,才会生成代码。通常,当我们使用类类型对象时候,类定义必须是可用的,但成员函数定义不必要出现。因此可以将类定义和函数声明放在头文件,普通函数和类成员函数定义放在源文件。模版则不同,模版实例化,需要知道成员函数的定义,因此模板的头文件通常既包含声明也包括定义。
  • 模板编译错误警告合适出现:第一个阶段在编译模板本身,给出简单的语法错误;第二阶段即使碰到模板使用时,检测参数是否正确,是否匹配;第三阶段模板实例化,这个阶段给出类型相关的错误,是否重载了相应的运算符等等。

类模版

类模板是用来生成类的蓝图。与函数模板不同,编译器不能为类模板推断模板参数类型,必须使用尖括号提供额外的参数来代替模板实参列表。

  • 实例化类模板:使用类模板,提供额外的信息也就是显示模板参数,用来绑定到模版参数,编译器根据这个实参来实例化特定的类。编译器从模板实例化类时候,重写类,将模板参数全部用实例的实参替换。实例形成的类是一个独立的类,不和任何其他的类型关联。就是通过模版生成了一个类。
  • 类模板引用其他类模板:通用通过实参传递实例化模板参数。
  • 模板成员函数:和其他任何类相同。定义在类模板之外的成员函数必须通过关键字template 后接参数列表。并且必须说明是属于哪个模板
  • 类模板成员函数的实例化:只有当程序使用的时候,才进行成员函数的实例化。使用类模板实际上就是实例化对应的成员,然后使用它而已,说到底都是进行了函数的调用。不论类还是啥,程序都是一步一步执行的,类增加了代码的可重复率。使用哪个成员函数就是调用哪个函数而已。这靠编译器把握。
  • 在类代码内简化模板类名的使用,但是在类模板外面必须使用类模板名(遇到类名才表示进入了作用域)。在类模板作用域内可以直接使用模板名,而不需要指定模板实参。这些都是规定,都是为了让编译器理解我们所写的是什么鬼东西。

    BlobPtr& operator++();//类内部声明或定义不需要添加模板参数

    template //类外面定义需要添加模板参数
    BlobPtr BlobPtr::operator++(int)
    {

    1. BlobPtr ret = *this; //进入类作用域,不用提供模板实参等价于BlobPtr<T> ret = *this;
    2. ++*this; // advance one element; prefix ++ checks the increment
    3. return ret; // return the saved state

    }

  • 类模板和友元:类模板与另一个(类或函数模版)减的友好关系是建立对应实例及其友好间的关系。

    template class BlobPtr;//声明类,因为下面友元声明需要用到
    template class Blob; //声明类,因为下面==用到了
    template

    1. bool operator==(const Blob<T>&, const Blob<T>&);//声明函数模版
  1. //类模板,成员函数定义类内和类外有区别。
  2. template <typename T> class Blob {
  3. //定义Blob类模板
  4. friend class BlobPtr<T>;//友元类
  5. friend bool operator==<T>//友元函数
  6. (const Blob<T>&, const Blob<T>&);//声明两个友元函数
  7. }
  8. Blob<char> ca;// BlobPtr<char> operator==(char)都是本对象友元,BlobPtr<char>成员可以访问ca私有部分。和普通类规则完全一模一样。
  • 通用和特定的模板友好关系:
  • 类模板的static成员:每个模板实例类都有自己的static成员,但是通过这个实例类的对象公用这些static成员,因此我们必须也将静态成员和在模板外部定义的函数一样定义成成模板。可以直接通过类类型的对象访问一个类模板的静态成员,也可以使用类作用域运算符直接访问成员。这在模板里面有大量应用,为了通过作用域访问,全部定义成静态成员。

类模版参数

模板参数名字可以使用任何名字。

  • 模板参数与作用域:和普通一样,模板参数隐藏外层作用域中声明的相同名字,但是模板内不能重用模板参数名。
  • 模板声明:模板声明必须包含模板参数,并且模板参数的名字不必和定义中相同。
  • 使用类的类型成员:

发表评论

表情:
评论列表 (有 0 条评论,595人围观)

还没有评论,来说两句吧...

相关阅读

    相关 编程模版

    泛型编程 面向对象编程(OOP)和泛型编程都能处理在编写程序时不知道类型的情况。不同之处在于:OOP能处理类型在程序运行之前都未知的情况;而在泛型编程中,在编译时就能获知

    相关 C#编程

    泛型:通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用。 例子代码: class Program     \{  

    相关 16-模板和编程

    模板和泛型编程 面向对象的编程和泛型编程都能够处理在编写程序时,不清楚类型的情况;面向对象的编程处理的类型在程序运行之前都是未知的,泛型编程在程序编译时,就可以清楚