嵌入式学习(十二)

        今天要学三个知识点:引用、内存分配及重載

一、引用 &

引用是C++对C的一个非常重要的扩充,引用的引入,重要的作用是用于参数和返回值,无需再考虑值传递地址传递的问题了

1.1 引用的概念

所谓引用,其实相当于给变量起个别名,但是,不会给引用分配内存空间:例如, 宋江的别名  及时雨

1.2 引用的定义

1> 定义格式:数据类型  &引用名 = 引用的目标;

例如:int num = 520;        int &ref= num;

2> 引用的注意事项

1、定义引用时,必须给引用进行初始化,否则会报错

2、系统不会给引用重新分配内存空间,引用与引用的目标是同一内存空间

3、引用的目标一旦确定,后期不能进行修改

4、一个目标可以有多个引用,多个引用都是一个内存空间

5、定义引用时,需要使用&来说明身份,但是使用引用时,跟使用目标用法一样

3> 总结&的用途

1、&后面跟变量名,表明取得该变量的地址

2、作为双目运算符,一个&表示按位与运算

3、作为双目运算符,两个&&表示逻辑与运算

4、定义引用时, 表明身份的象征,一个&表明是左值引用,两个&&表明是右值引用

5、对于一个&使用时,如果左侧有数据类型,表明正在定义引用,如果左侧没有数据类型,表明是取地址运算符

1.3 引用的基本使用

#include <iostream>

using namespace std;

int main()

{

    int num = 520;          //定义一个变量

    //定义一个引用,其目标为num

    int &ref = num;          //定义引用时必须初始化,否则报错

    cout<<"num = "<<num<<"      ref = "<<ref<<endl;      //值相同

    cout<<"&num = "<<&num<<"      &ref = "<<&ref<<endl;  //地址相同

    cout<<"size of num = "<<sizeof(num)<<"      size of ref = "<<sizeof(ref)<<endl;  // 所占内存大小一致

    cout<<"type of num = "<<typeid(num).name()<<"      type of ref = "<<typeid (ref).name()<<endl;  //数据类型一致

    ref = 1314;

    cout<<"num = "<<num<<"      ref = "<<ref<<endl;  //连个名字一块变

    //再定义一个新的变量

    int key = 999;

    ref = key;              //这是给ref的空间重新赋值,而不是给引用重新设置目标

    cout<<"&num = "<<&num<<"      &ref = "<<&ref<<"  &key = "<< &key <<endl;

    //再定义一个引用,目标为num

    int &ref1 = num;

    cout<<"&num = "<<&num<<"      &ref = "<<&ref<<"  &ref1 = "<< &ref1<<endl;

    //再定义一个引用,目标为ref

    int &ref2 = ref;            //没有多级引用,引用的引用也是一级引用,相当于给目标设置引用

    cout<<"&num = "<<&num<<"      &ref = "<<&ref<<"  &ref1 = "<< &ref1<<"  &ref2 = "<< &ref2<<endl;

    return 0;

}

1.4 引用作为函数参数

1> 引用作为函数参数,传递的是实参本身,本质上是地址传递

2> 相比于普通变量和指针做形参,传递效率更高

#include <iostream>

using namespace std;

//定义值传递函数

void swap1(int a, int b)

{

    //交换三部曲

    int temp = a;

    a = b;

    b = temp;

    cout<<"swap1:: a = "<<a<<" b = "<<b<<endl;       

}

//定义swap2函数

void swap2(int *p, int *q)

{

    int *temp = p;

    p = q;

    q = temp;

    cout<<"swap1:: *p = "<<*p<<" *q = "<<*q<<endl;     

}

//定义swap3函数

void swap3(int *p, int *q)

{

    int temp = *p;

    *p = *q;

    *q = temp;

    cout<<"swap1:: *p = "<<*p<<" *q = "<<*q<<endl;     

}

//定义引用传递函数

void swap4(int &a, int &b)

{

    //交换三部曲

    int temp = a;

    a = b;

    b = temp;

    cout<<"swap4:: a = "<<a<<" b = "<<b<<endl;            //520  1314

}

int main()

{

    int num = 520;

    int key = 1314;

    //1、值传递

    swap1(num, key);

    cout<<"调用swap1后:num ="<<num<<"  key = "<<key<<endl;  //520  1314

    //2、传递地址的值传递

    swap2(&num, &key);

    cout<<"调用swap2后:num ="<<num<<"  key = "<<key<<endl;  //520  1415

    //3、 地址传递

    swap3(&num, &key);

    cout<<"调用swap3后:num ="<<num<<"  key = "<<key<<endl;  //1314  520

    //4、引用传递

    swap4(num, key);

    cout<<"调用swap4后:num ="<<num<<"  key = "<<key<<endl;        //520  1314

    return 0;

}

1.5 引用作为函数的返回值

1> 引用作为函数的返回值是一个左值

2> 引用作为函数的返回值返回生命周期比较长的变量

1、静态局部变量

2、全局变量

3、堆区申请的空间内容

4、主调函数中通过地址传递进来的参数的内容

#include <iostream>

using namespace std;

//定义一个普通函数,普通函数的返回值是一个右值,只可读,不可写

int fun1()

{

    int num = 520;

    return num;

}

//定义指针函数

int *fun2()

{

    static int num = 666;

    return #

}

//定义引用函数

int &fun3()

{

    static int num = 1314;

    return num;        //返回堆区空间的引用

}

int main()

{

    cout<<"fun1() = "<<fun1()<<endl;        //520

    //fun1() = 1314;              //普通函数的返回值是一个右值

    cout<<"*fun2() = "<<*fun2()<<endl;        //666

    *fun2() = 999;                  //指针函数的返回值是一个左值

    cout<<"*fun2() = "<<*fun2()<<endl;        //999

    //定义引用接受函数返回的结果

    int &ref = fun3();

    cout<<"fun3() = "<<ref<<endl;        //1314

    fun3() = 555;              //引用函数的返回值是一个左值

    cout<<"ref = "<<ref<<endl;

    return 0;

}

1.6 常引用

1> 常引用引用的目标可以是非常变量,本质上包含目标不被修改

2> 不可以通过常引用更改目标的值,但是可以通过目标自身进行更改

3> 常引用常用来修饰参数和返回值,表示包含参数或函数返回值不被修改

4> 常引用可以引用右值

#include <iostream>

using namespace std;

//定义加法函数,形参加上const保护形参数据不在函数体内被修改

int Add(const int &m,const int &n)

{

//    m = 0;

//    n = 0;

    return  m+n;

}

int main()

{

    int num = 520;

    //定义一个常引用,目标为非常变量

    const int &ref = num;

    cout<<"ref = "<<ref<<endl;        //可以对数据进行读操作

    //ref = 1314;              //不能通过常引用对目标进行更改

    num = 1314;

    cout<<"ref = "<<ref<<endl;    //1314

//    int a = 3;

//    int b = 5;

    cout<<Add(3,5)<<endl;

    //常引用引用右值的案例1

    const int &r = 1314 + 100;

    //常引用引用右值的案例2

    const double &r1 = (double)num;      //const修饰的引用,可以引用临时值

    const int &r2 = Add(3,4);            //const修饰的引用,可以引用普通函数的返回值

    //double d = (double)num;

    return 0;

}

1.7 右值引用(了解)

1> 定义格式:数据类型 &&引用名 = 引用目标;

2> 左值引用的目标必须是一个左值,右值引用的目标必须是一个右值

3> 可以使用move函数进行将左值转换成右值

#include <iostream>

using namespace std;

int main()

{

    int num = 520;

    int &r1 = num;          //左值引用,能引用左值

    //int &r2 = 520;              //左值引用不能引用右值


    int &&r3 = 1314;          //右值引用可以引用右值

    //int &&r4 = num;            //右值引用不能引用左值


    int &r5 = r3;            //右值引用本身是一个左值

    int &&r6 = move(num);      //move函数的功能是将一个值转换成右值

    return 0;

}

1.8 引用作为结构体的成员

如果结构体中有引用成员,那么对该成必须进行初始化工作

#include <iostream>

using namespace std;

//声明一个结构体类型

struct Stu

{

    string name;

    int age;

    double &score;          //引用成员

};

int main()

{

    double s = 90;

    struct Stu s1 = {"zhangpp", 18, s};          //其他成员都可以不进行初始化,但是引用成员必须初始化

    cout<<"score = "<<s1.score<<endl;            //90

    return 0;

}

1.9 指针与引用的区别

1> 指针记录的是变量的地址,而引用变量本身

2> 定义引用时必须初始化,而定义指针不是必须初始化

3> 指针需要分配8字节的内存空间,而引用与目标是同一内存空间,无需额外分配

4> 指针可以有多级指针,但是引用只有一级引用

5> 指针后期可以更改指向,而引用一旦绑定后期就不能进行更改目标了

6> const修饰指针时,有修饰指向和值,而const修饰引用时,只有修饰值

7> 指针使用时,需要使用取值运算符进行解引用,而引用使用时跟目标的使用方式一致

8> 没有引用数组,但是有数组引用

#include <iostream>

using namespace std;

//函数形参使用数组接受,本质上是指针接收

void fun(int brr[], int n)

{

    cout<<"sizeof brr = "<<sizeof(brr)<<endl;      //8

    cout<<"sizeof brr = "<<sizeof (brr[0])*n<<endl;  //得到传入的数组大小 12

    cout<<"&brr = "<<&brr<<endl;

}

//函数形参使用数组引用接受数组,接受的就是数组本身

void gun(int (&ref)[3])

{

    cout<<"sizeof ref = "<<sizeof(ref)<<endl;    //12

    cout<<"&ref = "<<&ref<<endl;          //

}

int main()

{

    int arr[3] = {1,2,3};

    fun(arr,3);

    cout<<"&arr = "<<&arr<<endl;

    cout<<"sizeof arr = "<<sizeof (arr)<<endl;

    gun(arr);      //调用函数时,传递数组即可

    return 0;

}

二、C++中的动态内存分配和回收

1> C++也支持使用malloc、free来完成对堆区空间的申请和释放工作,该工作适用于对基本数据类型、结构体变量空间申请

2> C++是面向对象的编程,对于对象的空间申请有专门的关键字来完成,new和delete完成对象空间的申请和释放

2.1 单个内存的申请和释放

1> 申请格式:数据类型  *指针名 = new 数据类型;

例如:int *p1 = new int;                  //在堆区申请一个int大小空间的内存

2> 释放格式:delete 指针名;

例如:delete p1;

#include <iostream>

using namespace std;

int main()

{

    int *p1 = new int;        //在堆区申请一个int单位的空间

    cout<<"*p1 = "<<*p1<<endl;      //随机值

    *p1 = 520;                    //使用堆区内存空间

    cout<<"*p1 = "<<*p1<<endl;            //520

    //在堆区申请空间后,给初始值

    int *p2 = new int(1314);        //在堆区申请一个int单位的内存,并给定初始值为1314

    cout<<"*p2 = "<<*p2<<endl;        //1314

    //是否内存空间

    delete p1;

    p1 = nullptr;        //(void *)0

    delete p2;

    p2 = nullptr;

    return 0;

}

2.2 连续内存空间的申请和释放

1> 申请格式:数据类型 *指针名 = new 数据类型[个数];

例如:int *p1 = new int[5];                    //在堆区申请5个int大小的恐惧

2> 释放格式:delete [] 指针名;

例如:delete []p1;           

#include <iostream>

using namespace std;

int main()

{

    //在 堆区申请空间,不进行初始化工作

    int *p1 = new int[5];          //在堆区申请5个连续的int大小的空间

    for(int i=0; i<5; i++)

    {

        cout<<p1[i]<<" ";

    }

    cout<<endl;

    //使用堆区空间

    for(int i=0; i<5; i++)

    {

        p1[i] = 10+i;

    }

    for(int i=0; i<5; i++)

    {

        cout<<p1[i]<<" ";

    }

    cout<<endl;

    //在堆区申请空间并初始化

    int *p2 = new int[5]{1,2,3,4,5};

    for(int i=0; i<5; i++)

    {

        cout<<p2[i]<<" ";

    }

    cout<<endl;

    //释放内存空间

    delete []p1;

    p1 = nullptr;

    delete [] p2;

    p2 = nullptr;

    return 0;

}

练习:在堆区申请一个长度为5的数组,用于存放5名学生的成绩,自己封装函数完成,对5名学生成绩的录入、升序排序、输出

要求:使用new和delete完成

#include <iostream>

#include <istream>

using namespace std;

int main()

{

    int *p3=new int[5];        //在堆区申请空间

    //输入5名学生成绩

    for(int j=0;j<5;j++)

    {

        cin>>p3[j];

    }

    //输出

    for(int i=0;i<5;i++)

    {

        cout<<p3[i]<<" "<<endl;

    }

    //排序

    for(int k=1;k<5;k++)

    {

        for(int l=0;l<5-k;l++)

        {

            if(p3[l]>p3[l+1])

            {

                int temp=p3[l];

                p3[l]=p3[l+1];

                p3[l+1]=temp;

            }

        }

    }

    //输出

    for(int o=0;o<5;o++)

    {

        cout<<"p3[o]="<<p3[o]<<endl;

    }


    //释放内存空间

    delete [] p3;

    p3 = nullptr;


    return 0;

}

2.3 new\delete与malloc\free的区别(笔试面试题)

1> new申请空间时,可以给堆区空间进行初始化,而malloc申请时不能进行初始化

2> new\delete是关键字,而malloc\free是库函数

3> new申请空间时以数据类型为单位,而malloc申请空间时以字节为单位

4> new申请的空间返回的结果申请类型的指针,而malloc申请空间时返回void*类型,需要进行强转后使用

5> new申请空间时会调用构造函数,malloc不会(后期讲)

6> delete释放空间时,会调用析构函数,free不会(后期讲)

7> new、delete申请释放空间时,区分单个还是连续空间,而malloc不区分

三、C++对C的函数部分的扩充

3.1 函数重载

1> 在C语言中,同一作用域下不允许定义多个同名的函数,对于功能类似,但是只有数据类型不同的函数,也要定义多个不同名的函数,调用起来比较麻烦

2> C++中支持函数重载,即:在同一作用域下,可以定义多个同名的函数,但是要求参数列表必须不同

3> 所谓函数重载,是静态多态的一种,能够做到“一名多用”

4> 函数重载的要求:

1、函数名相同

2、形参列表必须不同:可以是参数个数不同、参数类型不同

3、作用域也要相同

4、跟返回值没有关系

5> 调用:当调用函数时,系统会根据传递的实参类型,自动匹配相应的重载函数

#include <iostream>

using namespace std;

//定义函数求两个数据的和

int sum(int m, int n)    //sumii

{

    return m+n;

}

//求两个小数的和

double sum(double m, double n) //sumdd

{

    return m+n;

}

//求两个字符串的和

string sum(string m, string n)

{

    return m+n;

}

//定义求三个整数的和

int sum(int m, int n, int k) //sumiii

{

    return m+n+k;

}

int main()

{

    cout << sum(2,5) << endl;            //7 调用第一个函数

    cout << sum(2.3,5.2) << endl;      //7.5  调用第二个函数

    cout << sum("hello ","world") << endl;      //hello world  调用第三个函数

    return 0;

}

练习:使用函数重载完成求两个整数的最大值、两个小数的最大值、三个整数的最大值、两个字符串的最大值,并完成调用(my_max)

#include <iostream>

using namespace std;

int my_max(int a, int b)

{

    if(a > b)

    {

        return a;

    }

    else

    {

        return b;

    }

}

int my_max(double a, double b)

{

    if(a > b)

    {

        return a;

    }

    else

    {

        return b;

    }

}

int my_max(int a, int b, int c)

{

    if(a > b && a > c)

    {

        return a;

    }

    else if(b > a && b > c)

    {

        return b;

    }

    else if(c > a && c > b)

    {

        return c;

    }

    return 0;

}

string my_max(string s1, string s2)

{

    if(s1 > s2)

    {

        return s1;

    }

    else

    {

        return s2;

    }

}

double sum(double a, int b)

{

    return a+b;

}

int main()

{

    cout<<my_max(3, 5)<<endl;

    my_max(2.3, 4.5);

    my_max(1, 2, 3);

    my_max("hello", "world");

    return 0;

}

3.2 函数默认参数

1> C语言中定义函数时,不允许设置默认参数,函数形参的值,必须全部由实参进行传递后使用,实参的个数必须跟形参个数保持一致

2> C++定义函数时,允许给定默认参数,即:如果该参数有实参进行传递,则使用实参传递进来的值进行使用,如果实参没有对该参数进行传递,则使用默认参数

3> 默认参数的设置原则:靠右原则,只有右侧的形形参设置了默认参数后,左侧的形参才能设置默认参数,否则报错,原因是,函数实参向形参传递时是靠左原则

4> 当函数默认参数跟函数重载同时出现时,可以定义重载参数个数小于带默认参数的函数,但是,调用时会出现混乱情况

5> 当主调函数写在被调函数定义之前时,需要对对被调函数进行函数声明,函数的默认参数,写在函数声明部分,函数定义部分就不需要加默认参数了

#include <iostream>

using namespace std;

int sum(int m = 50, int n=100, int k =200 );//对函数进行声明

//当有函数进行设置默认参数时,再定义参数个数小于上述函数时,需要注意,是否重复,如果重复,

//定义时,没有问题,但是调用该函数时,会出现混乱情况

int sum(int a, int b)

{

    return a+b;

}

int main()

{

    cout << sum(1,2,3) << endl;          //6  此时,形参的值全部都由实参进行传递

    //cout << sum(1,2) << endl;            //203

    cout<<sum(1)<<endl;                  //301

    cout<<sum()<<endl;                  //350

    return 0;

}

//被调函数的定义

int sum(int m , int n, int k  )

{

    return m+n+k;

}

3.3 哑元

1> 在C++中的函数中,允许将函数参数设置成哑元,即某个形参只有类型名,没有形参名,在函数体内也不使用该形参

2> 作用:哑元参数只起到占位作用,没有实质性的用途

3> 使用场景

1、在进行程序代码优化时,可能某个函数的某几个参数被优化掉了,但是,该函数已经在程序中调用多次,那么,此时就可以将这些参数设置成哑元,只起到占位作用,函数体内无需使用

#include <iostream>

using namespace std;

//将第二个设置成哑元,只起到占位作用

int sum(int m, int, int k)

{

    return m+k;

}

int main()

{

    cout << sum(2,3,5) << endl;          //10

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;

    cout << sum(2,3,5) << endl;


    return 0;

}

2、在运算符重载时,进行区分自增自减运算符的前置和后置时,必须使用哑元完成(后期讲)

3.4 内联函数(inline)

1> C++中,允许定义内联函数,内联函数会建议编译器在编译程序的时候,将内联函数在调用处进行展开,运行时直接执行函数体内容,提高函数调用效率

2> 使用要求:要求函数体调用频繁,并且函数体内容较小,递归函数不允许定义成内联函数

3> 内联函数定义格式:在定义函数前加关键字inline即可

#include <iostream>

using namespace std;

//定义一个内联函数

inline int sum(int m, int n)

{

    return m+n;

}

int main()

{

    cout<<sum(1,2)<<endl;

    return 0;

}

4> 有参宏和内联函数的区别

1、本质的区别:有参宏是宏替换,内联函数是函数调用

2、替换时机:有参宏替换发生在预处理阶段,内联函数替换发生在编译阶段

#include <iostream>

using namespace std;

#define MAX(x,y) x>y?x:y                //定义有参宏

//定义一个内联函数

inline int my_max(int x, int y)

{

    return x>y?x:y;

}

int main()

{

    int a = 3;

    int b = 2;

    int c;

    c = MAX(a++,b++);      //a++ > b++? a++:b++;  ===> 3>2?4

    cout<<"a = "<<a<<"  b = "<<b<<"  c = "<<c<<endl;          //5  3  4

    a = 3;

    b = 2;

    c = my_max(a++, b++);    //my_max(3,2)

    cout<<"a = "<<a<<"  b = "<<b<<"  c = "<<c<<endl;        // 4  3  3

    return 0;

}

四、C++对结构体的扩充

1> C语言中的结构体,仅仅只是属性(变量)的聚合体,不允许在结构体中定义函数,如果想要定义函数,需要使用函数指针,完成函数回调

2> C++中的结构体内可以包罗万象,既可以封装属性、也可以封装函数、还可以封装一个结构体。。。

3> C++中的结构体在定义属性时,可以直接给定初始值,而C语言中不行

4> C++中的结构体在定义结构体变量时,可以不用加关键字struct,而C语言中不可以

5> C++中的结构体可以有访问权限控制,C语言中的结构体没有访问权限控制

6> C++中的结构体可以被继承,C语言中的结构体不可以

#include <iostream>

using namespace std;

//定义一个人的结构体

struct Person

{

public:

    string name = "zhangsan";

private:

    int age = 0;

protected:

    double height = 1;

public:

    //在结构体中封装函数

    void show()

    {

        cout<<"name = "<<name<<"  age = "<<age<<"  height = "<<height<<endl;

    }

};

//定义一个老板结构体,继承自人这个结构体

struct Boss:public Person

{

    int money = 100000;

};

int main()

{

    struct Person p1;

    p1.show();

    //cout<<p1.age<<endl;    //私有属性不能被访问

    cout<<p1.name<<endl;    //公共成员,外界可以直接使用

    //使用boos结构体定义变量

    Boss b;

    b.show();

    b.money = 2000;

    return 0;

}

总结:

①引用,本质是什么?

②內存申请以释放,在c语言中有什么类的

③重载,是c++的一大特色。需要重点学习、复习、再学习、再复习

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容