6. C++引用(Reference)重点
6.1 什么是引用
在C++中,引入引用就是为了避免指针在使用过程中可能出现的问题(空指针,不合法内存),提高程序的安全性。
引用是一种关系型声明的类型,说明它跟别的变量的关系,它所声明的变量不占内存空间,通俗来讲,是已有变量的别名,来说明跟已有变量的关系,所以引用变量的类型要跟已有变量的类型保持一致。
引用引入分析:
6.2 引用的基本语法和注意事项
语法:type* --- 代表指针
type & ---> 引用类型 比如: int& ,char&
int --- int*(指针类型跟指向的类型保持一致)
在声明引用变量时,一定要说明跟其他变量的关系(必须初始化)
int i;
int& ri = i; //说明ri是i的别名,ri是引用类型声明变量
//[1] 引用的基本概念 type&
int i = 10;
int& ri = i; //必须初始化,要说明跟某个变量的关系
cout << "ri = " << ri << "i = "<< i<< endl;//10 10
cout << "&ri = " << &ri << " "<< "&i = " << &i << endl; //地址相同
ri = 20;
cout << "ri = " << ri << "i = "<< i<< endl; //20 20
i = 30;
cout << "ri = " << ri << "i = "<< i<< endl; //30 30
//[2] 注意事项
//[2.1]
int j = i; //int j = 100; ---> 其他的基本数据类型可以使用常量和变量来初始化
//int & rj = 100; //error 这种方式声明的引用,把它叫作普通引用,普通引用不能使用常量来初始化,内存地址不确定
//[2.2]
int &rj = j; //rj就是j的别名,跟j代表着同一块内存空间,引用一经初始化,不可更改
rj = i; //将变量i的值,赋值给rj,并不是初始化,使用类型声明一个变量时给它赋值,才是初始化
//[2.3]
int& rrj = rj; //引用可以多次被引用 多次被引用的结果就是一个变量有多个别名
cout << "rrj = " << rrj << "rj = " << rj << "j = " << j << "i = " << i << endl;
//[2.4]
// &符号前有数据类型时为引用 ,其他情况下,为取地址
6.3 引用的应用
对于引用而言,如果只在一个函数中使用变量,没有必要使用引用,使用基本的数据类型就可以,基本的数据类型除了在函数中可以用来声明变量之外,还可以做函数的形参、函数的返回值。
6.3.1 引用做为函数的形参
引用做为函数的形参,那么该参数采用是址传递
void changeA(int& aa) //引用做为函数形参时,可以不初始化
{
aa = 20;
}
void changeAA(int* p)
{
if(p != nullptr)
{
*p = 50;
}
}
void test(int a[]) //int* a
{
//sizeof(a) = ?
}
void test1(int a[1000]) //int* a
{
//sizeof(a) = ?
}
void test2(int a[5])
{
//sizeof(a) = ?
}
//编译器将以上三个函数都变成 void test2(int *a) void test1(int* a) void test(int *a)
//数组做为函数形参时,会自动退化为指针
int main()
{
//[1] 通过一个函数,来修改外部变量的值
int a = 10;
changeA(a);
cout << "a = " << a << endl;
changeAA(&a);
cout << "a = " << a << endl;
int b[5];
test(b);
test1(b);
return 0;
}
7. C++内联函数(inline)
1)内联函数引入分析
在C语言,对于函数的调用比较频繁,会大大增加栈开销,所以,对于短小的函数频繁调用时,C语言中,使用宏扩展来解决,可以很好的避免栈开销,但是,缺点就是缺少语法检测性。C++为了解决这个问题,C++语言新增关键字 inline,用于将一个函数声明为内联函数。在程序编译时,编译器会将内联函数调用处用函数体替换,从原理上类似于C语言中的宏扩展。采用内联函数可以有效避免函数调用的开销,程序执行效率更高。使用内联函数的缺点就是,如果被声明为内联函数的函数体非常大,则编译器编译后程序的可执行码将会变得很大。另外,如果函数体内出现循环或者其它复杂的控制结构的时候,这个时候处理这些复杂控制结构所花费的时间远大于函数调用所花的时间,因此如果将这类函数声明为内联函数,意义不大,反而会使得编译后可执行代码变长。通常在程序设计过程中,我们会将一些频繁被调用的短小函数声明为内联函数。
2)内联函数基本使用
在函数定义时,前面加上关键字inline,比如:
int Max(int a, int b);
inline int Max(int a, int b)
{
if(a > b)
{
return a;
}else
{
return b;
}
}
特别提示:inline 关键字应该与函数体放在一起,才会起作用,函数声明时,关键字可加或不加
8.C++默认参数(default argment)重点
默认参数:在函数定义实现时,给形参一个初始化值
语法:
返回值类型 函数名(type 形参变量名 = 默认值)
{
}
应用场景引入:通常情况下,函数在被调用时,形参从实参那里取得值,这一过程中,发生
了值拷贝操作,但是,对于 多次调用一个函数同一个实参时,C++给出了更好解决方案。给形参
一个默认值,就不需要从实参那里拷贝值了。
如果希望给形参一个初始值,这样子,在函数调用时,可以考虑给形参传值也可以使用默认值
注意事项:
//函数声明
void test2(int a = 10);
9 .占位参数
占位参数:跟默认参数不同,在函数定义时,形参只写类型,不写形参变量名
语法:
返回值类型 函数名(type ) //type --- int char
{
}
占位参数的使用场景比较少,只在运算符重载中可以应用
void test(int) //占位参数
{
}
void test1(char) //占位参数
{
}
int main()
{
int b = 10;
test(b); //带占位参数的函数调用时,要传入对应类型的参数值
return 0;
}
10.C++函数重载(overload) 重点
函数重载:函数名相同,函数的参数个数,函数的参数类型,函数的参数顺序形成的这些函数,那么说这些函数构成 函数重载
判断是否构成函数重载 规则:
1. 函数名相同 --- 前提条件
2. 函数的参数类型不同 || 参数个数不同 || 参数类型的顺序不同,三选一即可,均可构成重载
3. 函数的返回值不是做为函数重载判断的依据
C++是怎样来支持函数重载呢。而C语言不支持,原因在于C和c++,他们之间对源程序编译技术不一样,C++编译器编译源文件时通过底层倾轧(name mangling)技术,v--void i---int c -- char
底层倾轧(name mangling) ---- 将原有函数名 + 参数类型 ----> 在底层时,形成一个新的函数名,从底层,各个函数名还是不一样的。
比如:
void print(int data) ---> void print_i(int data)
void print(int data,int data2) ---> void print_ii(int data,int data2)
void print(char data) ---- void print_c(char data)
面试题:在C++程序中,如何引用C程序 或者 extern "C"的作用是什么
#ifdef __cplusplus
extern "C" {
#endif
//C程序代码
#ifdef __cplusplus
}
#endif
extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。