本文全部内容基于西安电子科技大学潘蓉老师的《面向对象程序设计》课程记录而成
函数默认参数
int area(int a=6) {
...;
}
area(7); // 正常调用
area(); // 等效于 area(6)
实参与形参的结合从左到右进行,故,指定默认值的参数必须在形参列表的最右端(以解决调用时省略参数的情况,防止参数串位)
int max(int a, int b=6, int c=123); // ok
int max(int a=1, int b, int c=123); // WRONG!
**推荐:在函数声明的时候就给出默认值,定义时不再给出。**不同编译器对默认值处理不同,可能由于声明、定义时都给出了默认值而报错,也可能支持声明和定义中使用不同的默认值(以先遇到的默认值为准)
重载函数和默认参数函数共同使用时可能出现二义性问题。
int max(int a, int b=6);
int max(int a);
引用(Reference)
基本用法
可以给变量起别名(alias),该别名就是变量的引用
<类型> &<引用变量名> = <原变量名> // &:引用声明符。表示后面的时引用变量名。原变量名需要是定义过的
例:
int max;
int &refMax = max; // refMax 就是 max 的别名。引用需要在定义时进行初始化
refMax = 123; // 用法和普通变量相同,也可以别名、原名混着用
max = 666;
int &refRefMax = refMax; // 可以进行引用的引用
refMax 和 max 在内存中处于同一地址。就只是两个名字指向了同一个地址。
引用声明后,不能再重新赋值、指向其他变量。
引用是有地址的,可以像使用普通变量一样用
int a, *p;
int &m = a; // &前面有类型就是引用的声明,没有的话就是取地址符
p = &a;
*p = 10; // a 的值就被修改为 10 了
对 const 的引用
int i=5;
const int &a = i; // a是一个引用,还是一个const。a称为【常引用】
a = 3; // 错误,a 是一个常量,其值不能被修改
i = 3; // 合法,因为 i 是个普通变量。这时再访问 a 得到的结果也是 3
几种非法声明
void &a // 引用需要引用一个已存在的变量,而并没有 void 类型的变量存在
int &a[6] = c; // c是一个数组名,本质是指向数组首元素的地址
int &*p = &a;
指针与引用的区别
- 指针是通过地址间接访问某变量,引用是通过别名直接访问某变量;
- 引用必须初始化,初始化后不能再称为其他变量的别名。
引用与函数
cpp 中很少使用独立变量的引用。引用主要用于 函数参数、函数返回值。
-
把变量的引用作为形参来调用函数
void swapint(int &a, int &b) { // a、b 就是实参 i、j 的引用了。初始化在调用时进行。 int tmp; tmp = a; a = b; b = tmp; } int main() { int i=3, j=5; swapint(i, j); // 执行完,i=5, j=3 return 0; }
引用形参 vs 指针形参:前者实参是变量,后者是地址,且后者需要单独创建空间用来保存指针
将引用类型作为函数的返回值
将函数的返回值定义为引用类型时,由于返回的是一个变量的别名,故这种情况下这样的函数可以作为左值。
int a = 4;
int &f(int x) {
a = a + x;
return a; // 返回的是 a 的引用
}
int main() {
int t = 5;
cout<<f(t)<<endl; // a=9.
f(t)=20; // 先调用,再赋值。 a=20.
return 0;
}
注意:返回引用类型的函数,必须返回某个类型的变量;返回的变量的引用,变量必须是全局变量 / 静态局部变量,即存储在静态区的变量
int &f(int &x) {
static int t=2; // 返回的变量的引用是局部静态变量
t=x++; // 先将 x 赋值给 t ,再 x++
return t;
}
int main() {
int a=3;
cout<<f(a)<<endl; // output:3, a=4, t=3
f(a)=20; // a=5, t=20
a+=5; // a=10
cout<<f(a)<<endl; // output:10, a=11, t=10
a=f(a); // a=11, t=11
cout<<f(a)<<endl; // output:11, a=12, t=11
return 0;
}
内联函数(inline)
被频繁调用的函数,可通过内联来提升性能(空间换时间)。
内联函数会在编译时被嵌入到函数调用处,以减少程序运行时频繁调用函数造成的资源开销(保护现场、断点地址入栈、跳转到被调用函数所在的内存地址执行,执行后从堆栈取断点地址跳回,继续执行)
直接在普通函数定义前加 inline 关键字即可
必须写在函数定义体前。声明前可加可不加,只在声明前加上是无效的。
调用内联函数时,编译器检查调用是否合法,然后将内联函数的代码直接替换掉函数调用代码,并用实参换形参。
内联需要慎重使用,函数体内出现循环、递归等复杂控制语句时不适合使用内联。另,编译器会根据函数的函数体来自动取消不值得的内联(内联,相当于对编译器的建议,而非绝对的指令)。
作用域运算符
float a = 1.23;
int main() {
int a = 111;
cout<<a<<" "<<::a<<endl; // 输出:111 1.23, ::a 是使用全局作用域中的变量 a
...
}
string
string2 = string1; // 赋值
string1 = string1 + string2; // 连接
string name[3] = {"...", "...", "..."}; // 字符串数组
动态分配/撤销内存(new/delete运算符)
new
new 从堆中分配一块空间,成功则将起始地址存入指针变量
<指针变量名> = new <类型>; // 想要啥样的类型就填啥。。
<指针变量名> = new <类型>(<初值>); // 要的变量的值
<指针变量名> = new <类型>[<元素个数>]; // 分配连续的空间,即数组
delete <指针变量名>
delete[] <指针变量名> // 释放连续的存储空间,即数组
动态整数存储空间
int *p = new int; // 用整型的指针指向这个空间
delete p;
int *p = new int(3); // 整数初始值为 3
delete p;
int *p; // 这里是没有办法动态确定数组长的
int pLength;
cin>>pLength; // 获取用户输入的数组长度
p = new int[pLength]; // 连续存储空间(数组空间),p 指向这个数组的首地址
delete[] p; // 这样就释放掉了,空中括号就是说明要释放的是数组空间