端午继续~
C++对C的扩展
目录
引用
普通变量名引用
对数组的引用
对指针的引用
对函数的引用
引用作为函数的参数
引用作为函数的返回类型
常引用
内联函数
函数重载
函数的默认参数(缺省参数)
默认参数和函数重载的二义性
占位参数
extern "C" 浅析
引用
本质:给变量名取别名,可以通过别名间接操作变量
&:引用说明符(不是取地址符)
给哪个变量取别名,就定义该变量
普通变量名引用
void Test(){int a = 0;//给变量a取一个别名叫b //&修饰变量为别名,b就是a的别名 --- 引用int& b = a;//必须初始化,这里&不是取地址,是引用}
注意:系统并不会为引用开辟空间,a,b代表同一个空间。
我们可以看一下地址
void Test(){int a = 0;//给变量a取一个别名叫b //&修饰变量为别名,b就是a的别名 --- 引用int& b = a;//必须初始化cout << "&a = " << &a << endl;cout << "&b = " << &b << endl;}
运行结果:
操作b等价于操作a
b = 10; 等价于操作了 a = 10;
对数组的引用
void Test_2(){int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);//给数组arr取别名my_arr//注意这里要将&my_arr用括号括起来,否则my_arr会先于[10]结合int (&my_arr)[10] = arr;//访问的方式是一样int i = 0;for (i = 0; i < sz; i++){cout << my_arr[i] << " ";}}
对指针的引用
void Test_3(){int num = 6;int* p = #//给num取别名为 my_num int*& my_num = p;//这里&的优先级高于*,所以可以不用带括号//访问时,与指针的访问方法是一样的cout << "num = " << *my_num << endl;}
对函数的引用
int main(){//对函数Test_3取别名为My_Test_3void(&My_Test_3)() = Test_3;//注意括号不可省略//访问My_Test_3();return 0;}
这么多例子,我们为什么要用引用,引用能解决什么问题?
咱往下走~
引用作为函数的参数
这是引用解决的主要问题,函数可以通过引用来操作外部变量
这里学过C语言的小伙伴可能注意到了,引用和指针十分相似
确实如此~
指针有一些操作比较麻烦地方:
形参是带指针*,并且实参要取地址,相对于来说比较麻烦。
所以在之后的学习中需要用到指针的地方尽量换成用引用
我们来对比一下,指针操作和引用操作,交换两个变量:
void My_exchange_1(int* x, int* y){int tmp = *x;*x = *y;*y = tmp;}void My_exchange_2(int& x, int& y){int tmp = x;x = y;y = tmp;}int main(){int a = 6;int b = 8;cout << "交换前:a = " << a << " b = " << b << endl;My_exchange_1(&a, &b);//指针My_exchange_2(a, b);//引用cout << "交换后:a = " << a << " b = " << b << endl;return 0;}
引用作为函数的返回类型
int& Ret(){int num = 5;return num;}int main(){int& b = Ret();//由于函数返回类型为int&,所以应该用别名类型来接收//这里相当于间接给num取了一个别名breturn 0;}
但是,我们都知道,局部变量一旦出了函数,就销毁了,所以这里的相当于你给一个已经释放了的空间取别名,这是不因该的,(会出现段错误)所以函数返回时不应该返回局部变量 !
常引用
简而言之就是给常量取别名
注:不能通过常引用修改内容
可以看到这里编译器会报错,为什么呢?
给10这个常量取别名为num,10是一个常量,但num是一个变量, C++格式要求十分严格,所以这里会报错。
如何解决呢?
const修饰以下即可
有什么用呢?
防止函数内部修改外部的值
观察以下代码:
void Print(const int& x){//x = 100 //errcout << "num = " << x << endl;}int main(){int num = 10;Print(num);return 0;}
我们知道,用引用作为形参可以节省空间,x 和 num 表示一个空间,这里写一个函数,功能就是遍历,但同时我希望他的功能只是遍历,如果一不小心修改了形参 x,也意味着 num 被修改,很危险,所以这里可以用常引用修饰 x,使 x 不可被修改,既提高了安全性,有节省了空间。
内联函数
必须在定义函数的时候使用inline关键字修饰,声明时不需要
string Print();int main(){cout << Print() << endl;return 0;}inline string Print(){string s1 = "端午节";string s2 = "快乐";return s1+s2;}
直观感受是不是和普通函数差不多?
其实有很大滴作用!
内联函数:在编译阶段,将内联函数中的函数体 替换 函数调用处 ,避免函数调用时的开销(类似于宏函数,内联函数只是编译阶段直接替换,宏函数在预处理阶段替换)。
注意事项:
1.不能存在任何形式的循环语句
2.不能存在过多的判断语句
3.函数体不能过于庞大
4.不能对函数取地址
所以,加上inline并不是使函数成为内联函数的充要条件,他只是必要条件
加上inline只是告诉编译器,希望使其成为内联函数,最终是不是,还是有编译器决定。
函数重载
表示同一个函数在不同的场景下可以有不同的含义
同一个作用域,函数的参数类型,个数,顺序不同都可以重载(返回类型不能作为重载的条件)。
void print_1(int a){cout << "int" << endl;}void print_1(char a){cout << "char" << endl;}void print_1(int a, char b){cout << "int char" << endl;}void print_1(char b, int a){cout << "char int" << endl;}int main(){print_1(50);print_1('a');print_1(60,'b');print_1('c',70);//根据参数自动匹配return 0;}
可以看到这里,函数名都一样,只是参数的类型,个数,顺序不一样,只要一种或多种,都可以重载。
重载函数的底层原理:实际上这些函数在linux编译之后会产生不同的函数名,然后根据参数自动匹配不同函数的。
函数的默认参数(缺省参数)
#include<iostream>using namespace std;void test_1(int a = 10, int b = 20)//这里的int a = 10,被赋值10,说明只是一个默认参数{cout << "a + b = " << a + b << endl;}//注意1//如果b设为默认参数,则从b往右的参数都必须设为默认参数//也就是说,此时a不受影响,c是必须设为默认参数//因为形参位置与实参位置保持一致,假设c没有默认参数//调用test_2(100,200),这时,a,b都被赋值,c没有赋值,编译器就会报错(缺少参数)void test_2(int a, int b = 20, int c = 30){cout << "a + b = " << a + b << endl;}//还有值得注意的一点:如果函数声明与函数定义分开,想要设置默认参数,只需在声明里设置即可,函数定义无需再设。int main(){test_1();//可以什么参数都不传,这里就会使用默认参数test_1(30);//将30这个实参传给a,这时,函数就不会使用默认参数,而是a = 30;test_1(40, 50);//同理,此时a,b的默认参数失效, a,b的值因为 40 , 50return 0;}
默认参数和函数重载的二义性
void fun(int a){cout << "a = " << a << endl;}void fun(int a, int b = 20){cout << "a = " << a << endl << "b = " << b << endl;}int main(){fun(10);return 0;}
此时编译器会报错:
什么意思呢?
就是说,此时调用fun(10)传入一个参数,既可以传给第一个fun,也可以传给第二个fun(第二个fun的第二个参数有默认参数,所以传入一个参数也正确),编译器不知到传给哪个fun,所以就报错,但如果这样调用fun(10,20)就是正确的,他会自动匹配到第二个fun。
占位参数
占位参数只有类型声明,没有参数名声明,一般情况下,函数体内部无法使用占位参数。
//形式一void test_1(int a , int){;//这里的int就是占位参数}//形式二void test_2(int a , int = 20){;//int = 20是占位参数,其实这里只是有一个隐形的参数名,但也只是表现形式,无法使用}int main(){test_1(10, 20);//传参依然要传两个,并且类型对应,否则报错(缺少参数)test_2(10);//占位参数有缺省值,所以可以只传一个参数test_2(10, 20);//当然传两个也okkreturn 0;}
也就是说,占位参数位置必须要传入参数,除非有缺省值。
至于怎么用,以后章节会细讲滴。
extern "C" 浅析
C++编译函数和C编译器编译函数的方式是不同的,假设定义一个Fun函数,经过C编译后会产生一个新的函数名叫Fun,而经过C++编译器编译后产生的新函数名可能(不同的C++编译器效果不一样)叫Funv,所以如果调用函数可能就会存在连接错误,那么为了能在C++的环境下调用C语言函数,就引入了extern "C",这部分代码也将按照C语言的编译方式取运行。
使用如下:
#if __cplusplus //检测当前是否是C++工程extern "C"//满足则执行这里{#endif//不满足则执行这里,因为不满足说明就是C语言工程,直接引用以下就可以extern//引入你想要实现的C语言的函数声明//...; #if __cplusplus}#endif
码字不易~