C++入门学习笔记1:引用、内联、动态分配内存

By | 2020年1月3日

函数默认参数

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;             // 这样就释放掉了,空中括号就是说明要释放的是数组空间

点击量:253

发表评论

邮箱地址不会被公开。 必填项已用*标注