C++提供了新的函数特性(这些特性C中没有)
内联函数
引用
按引用传参
默认参数
函数重载
函数模板
函数模板具体化
内联函数是为提高程序运行速度而提出的。(这是对C的改进)。
常规函数和内联函数编写方式相同,但C++编译器将它们与程序整合的方式是不同的。(因此要了解内联函数与常规函数的区别,必须深入到程序内部)
编译的最终产品是一个可执行的程序(这个程序由二进制的机器语言组成,可直接在机器上运行)。
在进行常规函数调用时,会将函数的参数复制到堆栈,并执行函数代码。(并且这个工程中,会进行多次内存的跳转,来实现主程序和调用函数之间的切换,这会有一定的开销。而对于内联函数,编译器直接将函数源码加入到主程序中,这样在程序执行的时候,就不会有内存的跳转,从而加快了程序运行的时间——但这肯定需要更多的内存,因为这相当于直接把源码赋值了过来,就是说只要有调用内联函数的地方,编译器就回用内联函数的源码替换内联函数。所以要有选择的使用内联函数,如果程序执行代码的时间比 函数调用耗费的时间长,那么使用内联函数不会对程序时间有太大的提升;但是反之,则可以 考虑使用内联函数,但是提升的性能也是有限的——如果函数被调用的次数很多,那么性能提升会很明显)。
在普通函数的 函数定义(和函数原型)之前加上 inline 关键字,那么该函数就被转换为了内联函数。(如果函数过大,或者 函数是递归的 则不考虑将其转换为内联函数。一般情况下,直接将函数定义在之前书写函数原型的位置,并加上inline关键字——也就是说,去掉函数原型,只保留函数定义,并且把函数定义提前到 之前函数原型所在的位置。)
内联函数和普通函数一样,都是按值来传递参数的。(这使得 C++的内联函数功能 ,优于 C语言的宏定义。C语言中使用 #define来实现宏定义,宏定义可以实现了内联函数相同的功能,但是宏不是通过传递参数来实现的,而是基于文本的直接替换来实现的,这使得宏编写时要求较严格。因此最好的方式,使用C++的内联函数来代替C语言的宏——实现函数功能的宏)
引用常用与函数传参(尤其是 结构 和 对象 向函数进行参数传递的时候)。(应用对函数 处理大型结构非常有用,并且在类的设计中也经常用到)
&符号 在C++/C 中用来只是变量的地址。(但是在C++中 赋予了&符号 另一个含义——用来声明引用,并且在声明引用的同时必须对其进行初始化,也就是必须声明的同时进行赋值,这一点不同于指针;并且引用本质相当于 const指针,一旦赋值便不会在改变,也无法人工的改变它,它就相当于变量本身其实。举例: int rats; int & rodents=rats; 第二个语句就声明了一个引用——rodents为rats的别名 ,这里的&不是地址运算符,而是作为类型标识符的一部分, int & 的含义是 声明一个 指向 int变量的 引用。在例子中 rats和rodents 指向相同的内存单元)
通过传递引用的方式,实现函数的参数传递,称为按引用传递(C语言中只有按值传递——指针本身也是 按值传递,传递是指针的地址,C++中有按值传递和按引用传递两种方式)
函数 引用传参 语法:void swapr(int & a,int &b)。(在将实参传递给形参的时候,相当于 声明了引用,同时对引用进行初始化。如果不希望对引用所指向的内存进行修改,可以使用const关键字,增加const关键字后的语法为:void swapr(const int & a, const int & b)。这种不希望对原值修改的情况一般使用按值传递即可,但是对于 结构或类 这些数据量比较大的类型,使用 const引用将能够极大地节省 创建副本带来的开销。当对函数进行参数传递的时候,按值传递,可以传递 表达式、常量、变量等;而 按引用传递,则实参必须是 变量才行,如果传递其他形式的参数,编译器报错——但是 当形参是 const引用时,允许 向函数传递 其他形式的实参 ,这时底层是通过生成临时变量的形式来接受 其他形式的实参的值,最终生成的是这个临时变量的引用,并且对这个引用的操作不会影响到原来的值,因为这个引用是对临时变量的引用,这种引用效果上其实等同于按值传参。在按 const引用传参的时候,如果传递给函数的 实参不是左值 或者 和const 形参的类型不匹配,这时,C++将创建 和形参类型一致的 匿名的临时变量 ,并将实参进行类型转换后 赋值给临时变量,最终对这个临时变量 进行引用—这些临时变量 只在函数调用期间存在,函数使用结束后 编译器便会将这些临时变量删除 )
所有可以被引用 的数据对象 被称为左值 (如 变量 数组元素 结构成员 指针等)。(常量——字符常量除外、表达式 就不属于左值)
使用const引用的优势:防止错误的修改原数据;const引用 可以同时 接受 const和非const实参——非const引用,只能接受 非cosnt实参;const引用使函数能根据情况生成临时变量,来接受右值数据
针对右值数据,C++新增了右值引用——右值引用使用的是 && 符号来声明,语法为:double && jref=3*a+b;。(右值引用 主要是 供 库设计人员使用,比如 实现移动语义等功能)
函数在返回结果的时候,也分为 按值返回和按引用返回。(按值返回,也相当于返回的是 结果的副本。返回引用,则是返回 结果的 别名)
声明指针时 会隐式的调用new 来分配内存。(auto_ptr模板 和 unique_ptr 可实现自动释放 new出来的内存)
一个类传递个另一个类 这称为类的继承。(前者是基类,后者是派生类。派生类继承了基类的方法。并且 基类的引用 同时也是派生类的引用)
对于基本的数据类型 cin使用的是引用。(例如 直接写成 cin>>n; 而不用写成 cin>>&n )
函数的默认参数 也是C++相对于C新增加的功能。(如果想使用默认参数,则必须 并且 执须 在函数原型中进行设置——不需要在函数定义中设置,并且在调用函数时,必须对形参一次赋值——中间不能间隔,比如 harpo(3, ,8);这个语句就是错误的,因为中间间隔了一个参数。通过使用默认参数,可以减少要定义的析构函数的数量 、方法 以及 方法重载的数量)
函数重载(也称为多态,意思就是允许函数有种形式——也就是 允许多个同名函数,这几个同名函数的参数列表不同;这多个同名函数相当于对名称进行了重载,所以这种语法形式也被称为函数虫重载。)也是C++ 相对C 新增加的功能。
函数重载的时候,允许出现 函数返回值类型不同 ,同时参数类表也不同的情况,也允许只有参数列表不同,但是不允许 只有数返回值类型不同——而参数列表完全相同。
默认参数的使用,可以在一定程度上代替一部分的函数重载。(默认参数 本质上 是一种特殊的函数重载)
C++底层,会将函数的 类型名等 按照一定的格式进行加密——这称为 名称修饰(或名称矫正)(也就是 重新编码——用指定的字符进行替换内容,程序员是看不到的,这种编码只是程序底层的一些操作)。
函数模板 是使用 泛型来定义函数(可以通过将 类型作为参数传递给模板——这种将类型作为参数的形式 被称为 参数化类型,从而 通过编译器 生成 具体类型的函数)。(函数模板使得 可以以泛型的方式编写程序——这种编写程序的方式成为通用编程)。
创建函数模板,必须同时以相同的语法 提供函数原型 和 函数定义 (并且,调用函数模板的时候,编译器会根据 传入参数的类型,来自动的生成对应类型的函数——这个过程是隐式的、自动的,不需要额外的定义。)。函数模板的语法为:
template <typename AnyType>
void Swap(AnyType &a ,AnyType &b){ AnyType temp;temp=a; a=b; b=temp;} (创建函数模板时,关键字 类型名 尖括号 这些元素都是必须的。其中的typename 也可以 class ,这两个关键字在这里都可以用)
通常将 函数模板 在头文件中进行创建。(并且,编译器最终生成的代码,只包含根据模板创建的 针对具体类型的 函数,而不包含原来的模板函数)
模板函数可以像普通函数一样 实现函数的重载,并且在模板函数的参数类表中 ,也可以出现 确定类型的参数 (比如 int型等)
对于一个函数名,可以有 普通函数、模板函数、显示具体画的模板函数、以及这三种类型的函数重载。(三种类型函数的有限顺序依次是 普通函数>具体模板函数>模板函数。其中具体模板函数的语法为 template <> void Swap<job>(job & ,job &); 该语句中的 <job> 是可有可无的)
C++中也可以显示的定义 模板要生成的具体类型的函数——这被称为函数的显示实例化。(只需提供 该类型的函数原型即可,函数原型的语法为 template void Swap<int>(int,int); )
在调用函数模板的时候,也可以 进行实例化的定义 语法为(cout <<Add<double>(x,m)<<endl;)
不能再同一个文件中使用 同一种函数类型的 显示实例化 和显示具体化。
在函数 传参的过程中, 无法实现 普通类型变量 向指针类型的自动转化 (这种转换 需要 人工 进行强制转换)
函数调用时 ,如果遇到多个匹配的原型或模板,则优先选择 非模板的函数 、更加具体的函数——如果出现多个函数的 匹配程度一致 编译器则无法区分 会报错。(在函数调用时 也可以 显示的指定 使用模板 或者 使用具体类型的模板。语法为:cout << lesser<>(m,n) <<endl; 该语句中的 <> 含义是 使用模板函数,而不适用常规函数。cout<<lesser<int>(x,y)<<endl; 该语句中的 <int> 含义是 使用模板函数 并将其 实例化为 int类型的函数。)
对于参数列表 为多个 参数的 函数,匹配时,最佳函数,必须是每个参数 的匹配程度 都不差于 其他函数的匹配,并且至少有一个参数的匹配优于其他参数。
在模板功能 刚开始添加进c++的时候,人们没有意识到 模板函数 和 模板类会这么有用。但是随着程序员们 不断探索 模板使用的 各种可能性——并提出修改建议,采用了现在的C++11 中 较为 完善的模板功能。
C++11 中提出了 decltype 关键字,该关键字用于模板中——指定 变量的类型。decltype的语法为:decltype(x) s; 该语句的含义是 定义一个变量 s,该变量的类型和 x的类型一致。(x 可以是 具体类型声明出来的变量 、可以是 一个表达式 、可以是一个函数调用 等等。当x 是函数调用的时候,代表 s的类型为 函数的返回类型,这个判断过程,只是编译器根据函数原型来实现的,其实并不需要真正的调用函数——虽然语法上 写的是 函数调用)
double xx=4.4
decltype ((xx)) r2=xx; 该语句表示的是 r2 xx的引用。(也就是说 r2的类型是 double &——(())两个括号 就代表了 r2 是引用类型)
decltype(xx) r2==xx;该语句表示的是 r2 是 xx的副本。(也就是说 如r2的类型是 double)
当需要对模板 的函数返回类型 使用 decltype时,不能直接将其加载函数名之前。(因为,decltype 需要用到函数内部一些变量,但是,在未进入函数之前,这些变量是不存在的。因此 C++ 提出了 后置返回类型的概念,也就是说,在函数的末尾来进行函数类型的定义。语法为 :
template<class T1, class T2>
auto gt(T1 x, T2 y) -> decltype(x+y) {;;;return x+y;} 。(这定义形式中,decltype 在 x,y变量声明语句之后,所有可以使用 x+y 来进行 函数返回类型的设置)