内联函数
为什么使用内联函数?
减少上下文切换,加快程序运行速度。
是对C语言中的宏函数的改进。
语法
#include <iostream> using namespace std;inline double square (double x) { return x*x; } int main () { cout<<square (2.2 )<<endl; }
其实就是在函数声明或者定义前加上关键字inline
。
引用变量
为什么要使用引用变量
主要用途是用作函数的形参。通过引用变量做参数,函数将使用原始数据,而不是其副本。
高效。
语法
引用实际上就是定义一个别名。看看下面代码:
#include <iostream> using namespace std;int main () { int a=50 ; int &b=a; cout<<"a:" <<a<<endl; cout<<"b:" <<b<<endl; cout<<"address of a:" <<&a<<endl; cout<<"address of b:" <<&b<<endl; b=100 ; cout<<"a:" <<a<<endl; cout<<"b:" <<b<<endl; int c=200 ; b=c; cout<<"a:" <<a<<endl; cout<<"b:" <<b<<endl; cout<<"c:" <<c<<endl; cout<<"address of a:" <<&a<<endl; cout<<"address of b:" <<&b<<endl; cout<<"address of c:" <<&c<<endl; }
a:50 b:50 address of a:0x61fe14 address of b:0x61fe14 a:100 b:100 a:200 b:200 c:200 address of a:0x61fe14 address of b:0x61fe14 address of c:0x61fe10
a和b的数据地址是一样的,这说明b相当于a的别名,我们改变b的值,也会改变a的值,而且后面我们试图将b转变为c的引用,但是行不通,b=c这个代码做的是赋值语句,相当于a=c.
引用和指针的区别
引用在声明的时候必须初始化
int &b;
这句话是不允许的。
引用的本质就是指针常量 。因为引用变量一旦初始化就不能更改。
int &b=a
和int* const p=&a
这两句中b
和*p
是一模一样的。
引用作为函数参数
#include <iostream> using namespace std;void swap (int &a,int &b) { int c; c=a; a=b; b=c; } int main () { int a=2 ; int b=3 ; swap (a,b); cout<<a<<b<<endl; }
可以看出把引用作为参数的函数,只需在声明时,把参数设置成引用即可。
const引用
试想一下,在参数传递过程中,我们把常数或者错误类型的实参,传给引用参数,会发生什么?常数不能修改,引用是错误的,正如int &a=2;
会报错一样;错误类型的实参,也不能直接引用。
为了解决这个事,c++允许临时变量的产生,原理是:我可以引用这个临时量,但是我得确保不给其赋值,即需要使用const 引用,const引用不允许变量发生赋值。
总结来说,临时变量的产生条件是,在传参给const引用参数时:
实参不是左值.(左值指的是const变量 和 常规变量。)
实参类型不正确且可类型转换。
所以说,为了使得引用参数传递的兼容性和安全性,请多使用const。
#include <iostream> using namespace std;double square (const double &a) { return a*a*a; } int main () { int a=3 ; cout<<square (3 +a)<<endl; }
可以看出来这里square函数可以接受非左值,类型错误的实参。
你可能觉得这样做很复杂,直接使用按值传参就行了。double square(double a)
和double square(const double &a)
,从效果来说,这两一样,但是我们使用第二种传参的好处是高效,试想一下我们同时传一个double类型的变量,const引用传参不需要数据的拷贝,更快。
右值引用
采用 && 来对右值做引用,这么做的目的是用来实现移动语义。
#include <iostream> using namespace std;int main () { double a=3.1 ; double && b=a*1.2 +2.3 ; cout<<b<<endl; b=3 ; cout<<a<<endl; cout<<b<<endl; }
结构引用
引用非常适合于结构和类
#include <iostream> using namespace std;struct apple { string name; double weight; }; apple & swap (apple &a, apple &b) { apple temp; temp=a; a=b; b=temp; return a; } int main () {apple a={"Bob" ,230 }; apple b={"Alice" ,190 }; swap (a,b);cout<<"a:" <<endl<<"name:" <<a.name<<endl<<"weight:" <<a.weight<<endl<<endl; cout<<"b:" <<endl<<"name:" <<b.name<<endl<<"weight:" <<b.weight<<endl<<endl; swap (swap (a,b),b);cout<<"a:" <<endl<<"name:" <<a.name<<endl<<"weight:" <<a.weight<<endl<<endl; cout<<"b:" <<endl<<"name:" <<b.name<<endl<<"weight:" <<b.weight<<endl<<endl; swap (swap (swap (a,b),b),b);swap (swap (a,b),b);cout<<"a:" <<endl<<"name:" <<a.name<<endl<<"weight:" <<a.weight<<endl<<endl; cout<<"b:" <<endl<<"name:" <<b.name<<endl<<"weight:" <<b.weight<<endl<<endl; }
a: name:Alice weight:190 b: name:Bob weight:230 a: name:Alice weight:190 b: name:Bob weight:230 a: name:Bob weight:230 b: name:Alice weight:190
swap()函数的返回值是一个引用变量,所以swap(swap(swap(a,b),b),b)
是合法的,且它等价于swap(a,b)
。
为何要返回引用?高效。 因为传统返回机制,会把返回结果复制到一个临时位置。
但是应该避免返回 函数终止时不再存在的内存单元引用。例如,避免返回临时变量的引用。
对于C语言的改进
用const引用传参传递 代替 按值传递。
对于要修改原始数据的函数,采用引用传参方式。
函数重载
默认参数
默认参数指的是函数调用中省略了实参时自动使用的一个值。
如何设置默认值?必须通过函数原型。
例如这里的void display(int a,int n=999);
这里n=999 就是默认参数
默认参数的作用是,不给这个参数传参时,他会采用默认值。
#include <iostream> using namespace std;void display (int a,int n=999 ) ;int main () { display (1 ); display (3 ,31 ); } void display (int a,int n) { cout<<a<<endl; cout<<n<<endl; }
函数重载
默认参数能让我们使用不同数目的参数调用同一个函数,而函数重载能让我们使用多个同名的函数。
函数重载的关键是函数的参数列表–也称函数特征标。如果两个函数的名字和特征标相同,那么这两个函数就完全相同。C++允许定义名称相同,函数特征标不同的函数,这就是所谓的函数重载。
#include <iostream> using namespace std;struct apple { string name; double weight; }; void print (int ) ;void print (double ) ;void print (char *) ;void print (apple &a,string str="apple" ,double w=100 ) ;int main () { int a=2 ; double b=3.14 ; char c[10 ]="hello!" ; apple d; print (a); print (b); print (c); print (d); print (d,"Alice" ,250 ); } void print (int a) { cout<<"int =" <<a<<endl; } void print (double a) { cout<<"double =" <<a<<endl; } void print (char * a) { cout<<"char* =" <<a<<endl; } void print (apple &a,string str,double b) { a.name=str; a.weight=b; cout<<"the name:" <<a.name<<endl; cout<<"the weight:" <<a.weight<<endl; }
int =2 double =3.14 char* =hello! the name:apple the weight:100 the name:Alice the weight:250
可以看出来print
函数有多个重载,现代编译器会根据你传递的参数类型,而选择最匹配的函数。
类型引用和类型本身视为同一个特征标,例如
double cube(double x);
和double cube(double &x);
是不能共存的。
匹配函数时,会区分const和非const变量,例如
void display(char* a);
和void display(const char* a);
是函数重载。
请记住是特征标,而不是函数类型使得可以对函数进行重载。例如
long gronk(int,float);
和double gronk(int,float);
是不能共存的。
函数重载的shortcoming
函数重载在实现同函数名多种功能的同时,也应当付出代价。
#include <iostream> using namespace std;void print (double ) ;void print (char *) ;int main () { int a=2 ; double b=3.14 ; char c[10 ]="hello!" ; print (a); print (b); print (c); } void print (double a) { cout<<"double =" <<a<<endl; } void print (char * a) { cout<<"char* =" <<a<<endl; }
double =2 double =3.14 char* =hello!
可以看出来这里print(a)
这里a是int类型,编译器会将其类型转化成double,然后调用对应函数。
但是,我们稍微改动一下代码
#include <iostream> using namespace std;void print (int ) ;void print (double ) ;void print (char *) ;int main () { int a=2 ; double b=3.14 ; char c[10 ]="hello!" ; print (a); print (b); print (c); print (12L ); } void print (int a) { cout<<"int =" <<a<<endl; } void print (double a) { cout<<"double =" <<a<<endl; } void print (char * a) { cout<<"char* =" <<a<<endl; }
这段代码中print(12L);
会报错,因为12L
是long类型的常量,如果我们试着强制匹配会发现,12L
既可以转化成int类型,也可以转化成double类型,从而编译器不知道到底调用哪个函数。
不要滥用函数重载
仅当函数基本执行相同的任务,但使用不同类型的数据时,才应当使用函数重载。