以下是本人整理的C++基础知识点,内容并不包含全面的C++知识,只是对C++重点内容、特点进行整理和归纳。
泛型程序设计方法
介绍:大量编写模板、使用模板的程序设计
特点
算法只实现一遍,适用于多种数据类型的处理
减少重复代码的编写
模板分类
函数模板和类模板
类型的参数化
数据的类型通过参数来传递
函数定义时,不指明具体的数据类型
函数调用时,编译器根据传入的实参自动推断数据类型
函数模板
函数模板介绍
建立一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位)
当发生函数调用时,再根据传入的实参来逆推出真正的类型
函数模板语法
写法1
template 返回值类型 函数名(形参列表){...}
写法2
template 返回值类型 函数名(形参列表){...}
class用于定义类型参数,和template用法一直
例子
template void Swap(T *a, T *b){ T temp = *a; }
template 关键字, 用于定义函数模板
typename 关键字,用来声明具体的类型参数
定义类型参数,也就是虚拟的类型,或者说是类型占位符
template 模板头
函数模板和普通函数的区别
普通函数:数据值的参数化
所用到的数据,类型确定,但值不确定
函数模板:数据类型和值的参数化
所用到的数据,类型和值都不确定,编译器根据实参来确定值和类型
类模板
介绍
类模板将类中的数据的类型参数化
可用于类的成员变量和成员函数
类模板的语法
类模板的声明
template class 类名
{};
template class 类名
{};
类模板的使用
实例化时,必须显式指明数据类型,编译器不会根据数据推演
例子
className obj(10, 20);
className obj(10, 15.5);
className obj(12.4, "东经180度");
与常规的函数重载类似
用途:普通数据类型和数组类型的区分
模板实参推断
通过函数实参来确定模板实参的过程叫模板实参推断
模板实参推断过程中的类型转换
普通函数和模板函数的类型转换
普通函数
转换发生时机
函数调用时
转换过程分类
算数转换
例如 int 转换为 float,char 转换为 int,double 转换为 int 等
派生类向基类的转换
向上转型
const 转换
将非 const 类型转换为 const 类型
数组或函数指针转换
用户自定类型的转换
函数模板
不显式指明模板实参时,仅能进行「const 转换」和「数组或函数指针转换」
显式指明模板实参时,跟普通函数一样
函数形参是引用类型时,数组不会转换为指针,类型参数仍然为数组类型
为函数模板显式地指明实参
func(10, 10);
特点
从左到右的顺序与对应的模板参数匹配
最右边的类型参数的实参可以省略(能够自动推断出来)
显式地指明实参时,可以应用普通函数的类型转换
函数模板可能无法处理某些数据类型
函数模板中的运算符不一定适用于所有数据类型
函数模板的显式具体化
语法
template<> const STU& Max(const STU& a, const STU& b);
template<> const STU& Max(const STU& a, const STU& b);
特点
是可选的,可去掉
template<> ,类型参数 已经被具体化,不需要
函数调用的调用顺序
非模板函数 > 显示具体化模板 > 常规模板
类模板的显式具体化
语法
声明类模板
template class Point{...} //普通类模板
template<> class Point{...} //显式具体化模板
template class Point{...} //部分显式具体化模板
类外定义函数
template void Point::fun(T1 x, T2 y){...} //普通类模板,需要带上模板头
void Point::fun(char* x, char*y){...} //显式具体化模板,不需要带上模板头
模板中除了包含类型参数,还可以包含非类型参数
例子
template class Demo{ };
template void func(T (&arr)[N]);
N 是一个非类型参数,用来传递数据的值,而不是类型
在函数模板中使用非类型参数
template void Swap(T a[], T b[], int len); //普通模板
template void Swap(T (&a)[N], T (&b)[N]);//含有非类型参数的模板
在类模板中使用非类型参数
template class Array{...}
参数N是非类型参数
非类型参数的限制
一个整数
传递给它的实参必须是一个常量表达式
一个指向对象或函数的指针(引用)
绑定到该指针的实参必须存在于静态数据区,不能位于堆栈区
模板和内存的关系
模板不会占用内存,最终生成的函数或者类才会占用内存
模板的实例化
由模板根据·实参类型生成特定的函数或类的过程
通过类模板创建对象时,一般只需要实例化成员变量和构造函数
模板函数只有被调用时才会被实例化
编译是针对单个源文件的
只要有函数声明,编译器就能知道函数调用是否正确
链接时将函数(类)调用和函数(类)定义对应起来
链接器的存在,函数声明和函数定义可以分开来
板的实例化是在编译期间,由编译器完成的
模板的声明和定义都应该放到同一个头文件中
模板的实例化是由编译器完成的,而不是由链接器完成的,这可能会导致在链接期间找不到对应的实例
编译器在实例化的过程中需要知道模板的所有细节
隐式实例化和显式实例化
隐式实例化
在调用函数或者创建对象时由编译器自动完成
显式实例化
通过代码明确地告诉编译器需要针对哪个类型进行实例化
好处是,可以将模板的声明和定义(实现)分散到不同的文件中
函数模板的显式实例化
template void Swap(T &a, T &b);//模板
template void Swap(double &a, double &b);//显式实例化
extern template void Swap(double &a, double &b);//显式实例化声明
类模板的显式实例化
template class Point{...}//模板
template class Point;//显式实例化
extern template class Point;//显式实例化声明
显式实例化的局限
每次模板使用发生变化,都要增加新的显式实例化
作为库时,开发者不知道使用者怎么调用
注意:尽量少使用显式实例化,要将模板的声明和定义都放到头文件中
类模板从类模板派生
template class A{...}
template class B : public A {...}
类模板从模板类派生
template class A{... }
template class B: public A {...}
类模板从普通类派生
class A{...}
template class B: public A{...}
普通类从模板类派生
template class A{...}
class B: public A {...}
函数、类、类的成员函数作为类模板的友元
template
class Tmpl
{
friend void Func1();
friend class A;
friend void B::Func();
};
函数模板作为类模板的友元
函数模板作为类的友元
类模板作为类模板的友元
类模板中可以定义静态成员
从该类模板实例化得到的所有同类型的类都包含同样的静态成员
该类模板定义之后,还需要在同一个文件内定义静态变量