多些使用中的为什么!
学习过程也是遗忘的过程!
泛型编程
模版、参数类型化,c++提供两种模板机制:函数模板和类模板。实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来代表。这个通用函数就成为函数模板。
注意: 模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。
函数模版
类模版、函数模版……
模版理解:如果说函数、类为一个小的集合,那么模版就是这一类集合的集合,
-
模版相关定义:函数模版/类模版,template <class 形参名,class 形参名,…>,随后紧跟着的是函数(类)就是函数模版(类模版),告诉编译器定义了Tname类型,function(Tname &a ,Tname &b)类型也需要传入,类型参数化;function(a,b)//自动推导参数类型function<int>(a,b);//指定参数类型
include<iostream> #include<string> using namespace std; template<class T>//告诉编译器 T是一个通用类型 //template<typename T> void changeNumber(T &a, T &b) { T temp = a; a = b; b = temp; } void main() { float a = 1.0f; float b = 0.4f; double c = 5; changeNumber(a,b); changeNumber<float>(a,b);// //changeNumber<float>(a,c); system("pause"); }
-
函数模版和普通函数调用规则:c++编译器优先考虑普通函数;可以通过空模板实参列表的语法限定编译器只能通过模板匹配;函数模板可以像普通函数那样可以被重载;如果函数模板可以产生一个更好的匹配,那么选择模板函数模版。和重载同时出现,优先使用普通函数(若普通函数没有实现,则error)。如果函数模版可以产生更好的匹配(更好操作如减少类型转换操作),优先调用模版。
函数模板的格式:template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表) { 函数体 } /* 函数模版调用 function(T &a, T& b)类型传入类型参数化 function(a,b)自动类型推导,按照a,b类型 function<T>(a,b)显示定义类型 */
-
函数模版和普通函数区别:函数模板不允许自动类型转化,普通函数能够自动进行类型转化函数模版也可以重载。普通函数可以进行函数类型隐式转换,函数模版不可以。
-
函数模版实现机理
预处理(Pre-processing) -> 编译(Compiling) ->汇编(Assembling) -> 链接(Linking)
a. 编译器并不是把函数模板处理成能够处理任何类型的函数
b. 函数模板通过具体类型产生不同的函数
c. 编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
类模版
-
类模版定义:
template<class 形参名,class 形参名,…> class 类名 { ... }; //方便浏览 template<class 形参名,class 形参名,…> class 类名 { ... };
类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。比如
template<class T> class A {public: T a; T b; T hy(T c, T &d); };
-
类模版做参数
类模版做为一个自定义数据类型进行做为参数传递
显示指定类型: void function(Person<int ,string> &p);
参数模版化: template<class T, class Tt>void function(Person<T , Tt> & P);
整体类型化: template < class T >void function( T &p);//类模板 template<class NameType, class AgeType> class Person{ public: Person(NameType name, AgeType age){ this->mName = name; this->mAge = age; } void PrintPerson(){ cout << "Name:" << this->mName << " Age:" << this->mAge << endl; } public: NameType mName; AgeType mAge; }; //类模板做函数参数 // void DoBussiness(Person<string,int>& p){ p.mAge += 20; p.mName += "_vip"; p.PrintPerson(); } int main(){ Person<string, int> p("John", 30); DoBussiness(p); system("pause"); return EXIT_SUCCESS; }
-
类模版的派生(继承)
//父类类模板 template<class T> class Base { T m; }; template<class T > class Child2 : public Base<double> //继承类模板的时候,必须要确定基类的大小 { public: T mParam; };
-
类模版的类外实现
类模版成员函数运行阶段才去创建,导致包含.h头文件,不会创建文件实现,无法解析外部命令(链接错误);
建议模版不用分文件编写,声明实现写到同一文件中:#include“classname.hpp”class Person{ public: Person(T1 name, T2 age); void showPerson(); T1 mName; T2 mAge; }; //类外实现 template<class T1, class T2> Person<T1, T2>::Person(T1 name, T2 age){ this->mName = name; this->mAge = age; }
-
类模版遇到友元函数
类内模版遇到友元函数类内实现例子:friend void functionname(classname<T,T> &P)
类内模版遇到友元函数类外实现例子:
template<class T1,class T1>class classname
template<class T1,class T1>void functionname(classname<T,T> &P);
模版函数声明:A. 提前声明;B. <>模版声明
类型转换
(type) expression
const_cast<type> (expr): const_cast 运算符用于修改类型的 const / volatile 属性。除了 const 或 volatile 属性之外,目标类型必须与源类型相同。这种类型的转换主要是用来操作所传对象的 const 属性,可以加上 const 属性,也可以去掉 const 属性。
dynamic_cast<type> (expr): dynamic_cast 在运行时执行转换,验证转换的有效性。如果转换未执行,则转换失败,表达式 expr 被判定为 null。dynamic_cast 执行动态转换时,type 必须是类的指针、类的引用或者 void*,如果 type 是类指针类型,那么 expr 也必须是一个指针,如果 type 是一个引用,那个 expr 也必须是一个引用。
reinterpret_cast<type> (expr): reinterpret_cast 运算符把某种指针改为其他类型的指针。它可以把一个指针转换为一个整数,也可以把一个整数转换为一个指针。
static_cast<type> (expr): static_cast 运算符执行非动态转换,没有运行时类检查来保证转换的安全性。例如,它可以用来把一个基类指针转换为派生类指针。
类型转换
静态
- 继承之间类型转换:
用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
上一篇有向上、向下转型 - 基本类型转换可以(慎重)
动态
- 基础类型不可以
- 继承之间可以