day43——C++对C的扩充

四、bool类型

1> C语言是不支持bool类型的,有关bool类型的操作,C语言中使用的都是整型。但是C++中支持bool类型

2> bool类型的值只有两个 一个是真 true(1)表示和假false(0)表示。这里true和false都是关键字,不可以当做标识符

3> 在所有整数中,所有非0数字都表示真,只有0表示假

4> bool类型的数据,默认是使用数字表示真假的,如果要使用单词表示真假,需要使用关键字 boolalpha 。如果还想继续使用数字表示真假,则需要使用关键字 noboolalpha 进行转换

5> bool类型所占内存的大小为1字节,本质上使用1bit表示就足够了,但是计算机分配内存的基本单位是字节,所以bool占1字节的内存

#include <iostream>
using namespace std;
int main()
{
    bool b1 = -10;
    bool b2 = 0;
    bool b3 = 10;
 
    //bool类型的数据默认使用数字表示真假
    cout<<"b1 = "<<b1<<endl;         // 1
    cout<<"b2 = "<<b2<<endl;         // 0
    cout<<"b3 = "<<b3<<endl;         // 1
    cout<<"***************************************"<<endl;
 
    //对bool类型的变量用单词赋值
    bool b4 = true;
    bool b5 = false;
    cout<<"b4 = "<<b4<<endl;         // 1
    cout<<"b5 = "<<b5<<endl;         // 0
    cout<<"***************************************"<<endl;
 
    //使用单词表示bool类型
    cout<<boolalpha<<"b1 = "<<b1<<endl;         // 1
    cout<<"b2 = "<<b2<<endl;         // 0
    cout<<"b3 = "<<b3<<endl;         // 1
    cout<<"***************************************"<<endl;
 
 
    //继续使用数字表示真假
    cout<<noboolalpha<<"b1 = "<<b1<<endl;         // 1
    cout<<"b2 = "<<b2<<endl;         // 0
    cout<<"b3 = "<<b3<<endl;         // 1
    cout<<"***************************************"<<endl;
 
    return 0;
}

五、引用(reference)

5.1 引用的引入

1> 在C语言中,进行数据传递的方式有两种,分别是值传递和地址传递,对于数据的传递都需要在被调函数中设置一个载体,对主调函数中的数据进行间接访问

2> 在C++中引入的引用的概念,就没有值传递和地址传递的区分了,直接传递的就是主调函数中的实参本身,并且被调函数中不需要申请载体的空间,直接对实参的值进行操作

3> 引用相当于给内存空间起个别名。

5.2 引用的定义格式

  类型名  &引用名  =  目标名;
      例如:int num = 520;
            int &ref = num;      //定义一个引用,并指向一个num目标
    对&又进一步使用,&的使用方式
        1、&表示单目运算符,取地址运算,后面紧跟一个变量
        2、&&表示双目运算符逻辑与运算
        3、&表示双目运算符,按位与运算
        4、&表示定义引

5.3 引用的注意事项

1、引用的使用跟普通变量的使用一样,跟引用的目标一样正常使用
2、引用在定义时,必须用目标对其进行初始化,否则会报错
3、引用和目标的类型必须保持一致(也可以不一致,后期继承和多态时讲父类指针或引用可以指向子类对象时)
4、引用一旦定义并指向目标后,后期就不能再更改目标了
5、一个目标可以定义多个引用,多个引用与目标都是同一个东西

#include <iostream>
 
 
using namespace std;
 
 
int main()
{
    int num = 520;        //在内存中申请4个字节,存储数据为520
 
 
    int &ref = num;     //此时给num定义一个引用,后期这两个都是同一个东西
    //int &r;              //定义引用不初始化会直接报错
    //string &re = num;       //定义引用时,类型必须与目标保持一致
 
 
    cout<<"num = "<<num<<"  ref = "<<ref<<endl;    //值相同
    cout<<"&num = "<<&num<<"  &ref = "<<&ref<<endl;    //地址相同
    num = 1314;       //对num进行改变
    cout<<"num = "<<num<<"  ref = "<<ref<<endl;    //值相同
    ref = 666;
    cout<<"num = "<<num<<"  ref = "<<ref<<endl;    //值相同
 
 
    int &ref2 = ref;            //给引用定义一个引用
    int &ref3 = num;            //给一个目标定义多个引用
    cout<<"num = "<<num<<"  ref = "<<ref<<"  ref2 = "<<ref2<<"   ref3 = "<<ref3<<endl;    //值相同
    cout<<"&num = "<<&num<<"  &ref = "<<&ref<<"  &ref2 = "<<&ref2<<"   &ref3 = "<<&ref3<<endl;    //值相同
 
 
    cout<<"sizeof(num) = "<<sizeof(num) <<"   sizeof(ref) = "<<sizeof(ref)<<endl;   //大小相同
    
    //验证引用的目标一旦指定,就不能再更改
    int key = 12345;
    //ref = key;          //? 该语句是将key的值赋值给ref也就是赋值给num,并不是将ref重新指向key 
    //&ref = key;           //? 报错,&ref是取得ref的地址,不能对地址常量赋值
    //int ref = key;         //? 报错  ref引用重复定义
 
 
    return 0;
}

5.4 常引用 const

1> 对于变量而言,可以是普通变量,也可以是常变量

2> 对应的引用也可以是普通引用和常引用

3> 有四种引用与目标的搭配

1、普通引用 普通变量

2、普通引用 常变量

3、常引用 普通变量

4、常引用 常变量

#include <iostream>
using namespace std;
int main()
{
    //普通引用目标为普通变量没有问题
    int num = 520;     //普通变量    对数据可读可写
    int &ref1 = num;   //普通引用    对数据可读可写
    /******************************************/
 
 
    //由于目标本身具有常属性,而引用是一个变量,所以报错,不能将普通引用目标为常变量
    const int key = 1314;      //常变量      对数据可读不可写
    //int &ref2 = key;            //普通引用
    /******************************************/
    
    //常引用的目标可以是普通变量
    int value = 666;          //普通变量   变量自身对数据可读可写
    const int &ref3 = value;  //常引用     引用对数据的处理可读不可写
    cout<<"ref3 = "<<ref3<<endl;     //可读
    //ref3 = 999;                  //不可写
    /******************************************/
    
    //常引用可以引用的目标为常变量
    const int number = 999;        //常变量   对数据可读不可写
    const int &ref4 = number;      //常引用   对数据可读不可写
 
 
    return 0;
}

5.5 引用与指针的关系

指针变量也是一个变量,可以定义一个指针变量的引用,但是一般不对指针变量定义引用

#include <iostream>
using namespace std;
int main()
{
    int num = 520;        //普通变量
    int *ptr = &num;      //定义指针变量指向普通变量
    int * &ref = ptr;       //定义了一个指针变量的引用
 
 
    cout<<"num = "<<num<<endl;          //使用值
    cout<<"*ptr = "<<*ptr<<endl;        //使用指针变量
    cout<<"*ref = "<<*ref<<endl;        //使用指针的引用
 
 
    return 0;
}

5.6 引用作为函数的参数

1> 引用作为函数的参数,传递的是实参本身,没有值传递和地址传递之说

#include <iostream>
using namespace std;
//定义交换函数1,完成值传递
void swap1(int num, int key)
{
    int temp = num;
    num  = key;
    key = temp;
 
 
    cout<<"swap1:: num = "<<num<<"  key = "<<key<<endl;
}
 
 
//定义交换函数2,接受地址进行操作
void swap2(int *p, int *q)
{
    int *t = p;
    p = q;
    q = t;
    cout<<"swap1:: *p = "<<*p<<"  *q = "<<*q<<endl;
}
 
 
//定义交换函数3,接受地址进行操作
void swap3(int *p, int *q)
{
    int t = *p;
    *p = *q;
    *q = t;
    cout<<"swap1:: *p = "<<*p<<"  *q = "<<*q<<endl;
}
 
 
//定义交换函数4,完成地址传递,形参使用引用接受
void swap4(int &num, int &key)
{
    int temp = num;
    num  = key;
    key = temp;
 
 
    cout<<"swap1:: num = "<<num<<"  key = "<<key<<endl;
}
 
 
 
 
int main()
{
    int num = 520;
    int key = 1314;
 
 
    //调用交换函数1
    swap1(num, key);              //1314   520
    cout<<"main:: num = "<<num<<"   key = "<< key<<endl;    //520  1314
    cout<<"**************************************************"<<endl;
 
 
    //调用交换函数2
    swap2(&num, &key);           //1314   520
    cout<<"main:: num = "<<num<<"   key = "<< key<<endl;    //520  1314
    cout<<"**************************************************"<<endl;
 
 
    //调用交换函数3
    swap3(&num, &key);           //1314   520
    cout<<"main:: num = "<<num<<"   key = "<< key<<endl;    //1314  520
    cout<<"**************************************************"<<endl;
 
 
    //调用交换函数4
    swap4(num, key);              //520  1314
    cout<<"main:: num = "<<num<<"   key = "<< key<<endl;    //520  1314
    cout<<"**************************************************"<<endl;
 
 
 
 
    return 0;
}

5.7 引用作为函数的返回值

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

2> 跟指针作为函数的返回值一样,必须返回一个生命周期比较长的变量

3> 能够返回的类型

1、一定不能返回局部变量
2、全局变量
3、静态局部变量
4、堆区申请的空间中的值

#include <iostream>
 
 
using namespace std;
//引用作为函数的返回值,返回的是一个左值
int &fun()
{
    //int num = 520;
 
 
    //return num;        //返回局部变量的空间是不合法的
 
 
    static int key = 520;
 
    return key;          //返回生命周期比较长的数据
}
 
int main()
{
    fun() = 1314;           //引用函数作为左值使用
 
 
    cout<<"fun() = "<<fun()<<endl;      //1314
 
 
    int &ref = fun();      //相当于给函数中的key又在主函数中起个别名
    ref = 666;
    cout<<"fun() = "<<fun()<<endl;      //666
 
 
    return 0;
}

5.8 数组的引用

1> C++中不支持引用数组

2> C++中支持数组的引用

3> 定义格式: 数据类型 (&引用名) [数组长度] = 数组名;

#include <iostream> 
using namespace std;
//定义fun1函数
void fun1(int arr[], int n)
{
    cout<<sizeof(arr)<<endl;          //8
    cout<<"数组目前中的数据为:";
    for(int i=0; i<n; i++)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
 
 
}
 
 
//定义fun2函数
void fun2(int *arr, int n)
{
    cout<<sizeof(arr)<<endl;          //8
    cout<<"数组目前中的数据为:";
    for(int i=0; i<n; i++)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
 
 
}
 
 
//定义fun3函数
void fun3(int (&arr)[8], int n)
{
    cout<<sizeof(arr)<<endl;          //32
    cout<<"数组目前中的数据为:";
    for(int val:arr)
    {
        cout<<val<<" ";
    }
    cout<<endl;
 
 
}
 
 
 
 
int main()
{
    int arr[8] = {1,3,5,8,7,6,4,2};
 
 
    //调用函数传递该数组
    fun3(arr, 8);
 
 
    return 0;
}

5.9 右值引用

1> 上面描述的引用都是左值引用

2> 左值:有内存空间的容器称为左值,表现形式有变量、堆区空间、字符串常量

右值:没有内存空间的数据,表现形式有常量、表达式的结果、值返回函数的返回值、将亡值

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

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

#include <iostream>
using namespace std;
int main()
{
    int num = 520;      //其中num为左值   520为右值
 
 
    int &ref1 = num;         //定义左值引用引用左值的空间
    int &&ref2 = 520;         //定义右值引用引用右值的空间
 
 
    //int &ref3 = 520;        //左值引用不能绑定右值
    //int &&ref4 = num;         //右值引用不能绑定一个左值
 
 
    int &ref5 = ref2;          //定义一个左值引用,引用一个右值的引用
 
 
    int &&ref6 = move(num);       //将左值移动成右值,进行使用
 
 
    return 0;
}

5.10 指针和引用的区别

1> 指针定义时需要使用*号,引用定义时需要使用&

2> 指针取值需要使用*号运算符完成,引用使用时直接跟目标使用方式一致

3> 指针定义时,需要给指针分配内存空间8字节,引用定义时不需要分配内存空间,引用使用的是目标的空间

4> 指针初始化后,可以改变指针的指向,但是引用初始化后,不能在改变目标了

5> 指针有二级指针,但是引用没有二级引用

6> 有空指针,但是没有空引用

7> 指针进行偏移运算时是对内存地址的偏移,而引用进行偏移时,就是对目标值的偏移

8> 指针不能指向右值,但是右值引用的目标可以是右值

9> 指针定义时可以不初始化(野指针),引用定义时必须初始化

10> 指针可以有指针数组,但是引用不能定义引用数组

六、堆区空间的申请和释放

6.1 概述

1> 在C语言中,对于堆区空间的申请和释放,使用的是malloc和free函数,C++中也可以继续使用

2> 在C++中,提供了更加操作方便的关键字 new和delete用于堆区空间的申请和释放

6.2 new和delete

1> new和delete申请和释放堆区空间时,分为单个空间的申请和释放以及连续空间的申请和释放

2> 单个空间的申请和释放

申请: 数据类型 * 指针名 = new 数据类型;

释放: delete 指针名;

#include <iostream>
 
 
using namespace std;
 
 
int main()
{
    //从堆区申请一个int类型的空间数据
    int *p1 = new int;
    cout<<"*p1 = "<<*p1<<endl;         //随机值
    *p1 = 520;                         //使用堆区空间
    cout<<"*p1 = "<<*p1<<endl;         //520
 
 
    // 从堆区空间申请double类型的数据
    double *p2 =  new double(3.14);
    cout<<"*p2 = "<<*p2<<endl;            //3.14
    
    
    //释放堆区空间
    delete p1;
    delete p2;
 
 
    return 0;
}

3> 连续空间的申请和释放

申请: 数据类型 *指针名 = new 数据类型[元素个数];

释放:delete []指针名;

#include <iostream>
 
 
using namespace std;
 
 
int main()
{
    //从堆区申请一个int类型的空间数据
    int *p1 = new int;
    cout<<"*p1 = "<<*p1<<endl;         //随机值
    *p1 = 520;                         //使用堆区空间
    cout<<"*p1 = "<<*p1<<endl;         //520
 
 
    // 从堆区空间申请double类型的数据
    double *p2 =  new double(3.14);
    cout<<"*p2 = "<<*p2<<endl;            //3.14
 
 
 
 
    //释放堆区空间
    delete p1;
    delete p2;
 
 
    cout<<"***********************************************"<<endl;
    //连续申请5个空间的int类型
    int *p3 = new int[5];        //没有初始化,默认都是随机值
    for(int i=0; i<5; i++)
    {
        cout<<p3[i]<<"  ";
    }
    cout<<endl;
 
 
    //连续申请空间并初始化
    int *p4 = new int[5]{3,7,2,1,6};
    for(int i=0; i<5; i++)
    {
        cout<<p4[i]<<"  ";
    }
    cout<<endl;
    
    //释放空间
    delete []p3;
    delete []p4;
 
 
    return 0;
}

练习:要求使用new和delete完成,在堆区申请一个空间,存储8名学生的成绩,完成这些学生的成绩录入、输出、升序排序等操作

#include <iostream>
using namespace std;
void inputScores(int *scores, int size) {
    cout << "请输入" << size << "名学生的成绩:" << endl;
    for (int i = 0; i < size; i++) {
        cin >> scores[i];
    }
}
void outputScores(int *scores, int size) {
    cout << "学生的成绩为:" << endl;
    for (int i = 0; i < size; i++) {
        cout << scores[i] << " ";
    }
    cout << endl;
}
void sortScores(int *scores, int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = i + 1; j < size; j++) {
            if (scores[j] < scores[i]) {
                int temp = scores[j];
                scores[j] = scores[i];
                scores[i] = temp;
            }
        }
    }
}
int main() {
    int size = 8;
    int *scores = new int[size];
    inputScores(scores, size);
    outputScores(scores, size);
    sortScores(scores, size);
    cout << "排序后的学生成绩为:" << endl;
    outputScores(scores, size);
    delete[] scores;
    return 0;
}

引入算法库的相关使用

#include <iostream>
#include<algorithm>
 
 
using namespace std;
 
 
void stu_input(int *stu)
{
    for(int i=0;i<8;i++)
    {
        cout<<"请输入第"<<i+1<<"位学生的成绩"<<endl;
        cin>>stu[i];
    }
}
void stu_output(int *stu)
{
    for(int i=0;i<8;i++)
    {
        cout<<"第"<<i+1<<"位学生的成绩为:"<<stu[i]<<endl;
    }
}
 
 
//定义全局函数当做排序的策略
int compare(int a, int b)
{
    return a>b;
}
 
 
int main()
{
    int *stu=new int[8];
    stu_input(stu);
    stu_output(stu);
   //调用算法库中的相关函数,默认是降序排序
    sort(stu, stu+8, compare);         //加了排序策略的排序函数
    cout<<"排序后结果为:"<<endl;
    stu_output(stu);
    return 0;
}

6.3 new\delete与malloc\free的区别

1> new和delete是关键字,而malloc和free是函数,需要包含相关的库

2> 使用new申请空间后,申请什么类型就是什么类型的地址,而malloc申请的结果是void*需要具体转换

3> new申请空间时,可以初始化,malloc申请空间时不能初始化

4> new和delete申请和释放空间时,单个和连续的操作是不同的,而malloc和free是不区分单个和连续申请释放的

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

6> new申请空间时会自动计算所需空间的大小,而malloc申请空间时需要手动计算大小

7> new关键字中封装了malloc函数,delete关键字中封装了free

9> new在申请对象的空间时会调用该类的构造函数,malloc不会

10> delete在释放对象空间时,会自动调用该类的析构函数,free不会

七、C++中的结构体

1> C语言中的结构体仅仅只是属性的聚合体,都只能封装一些变量

2> C++中的结构体,可以包罗万象(变量、函数、类型)

3> C语言中结构体定义变量时需要加上struct,而C++中不需要加struct

4> C语言中的结构体在声明时,是不允许给成员变量初始值的, 而C++中的结构体可以

5> C语言中的结构体时不能继承的,C++中的结构体可以继承

6> C语言中的结构体中所有成员变量都是公共的,外界可以通过结构体变量进行访问,而C++中的结构体中的成员变量是可以加访问权限的,分为公共权限、受保护权限、私有权限。只有公共权限的外界能够访问

#include <iostream>
 
using namespace std;
struct Person
{
    //如果不给设置权限,默认都是公共权限
    string name;       //姓名
    int age = 100;           //年龄
 
    //C++中的结构体,可以封装函数
    void speaker();            //结构体内声明,结构体外定义
    
private:                //该关键字后面的变量或者函数属于私有权限
    int money = 10000;
 
public:
    void set_money(int m);    //结构体内声明
    
protected:
    string skill = "C++";          //技能
};
 
//结构体外定义成员函数
void Person::speaker()
{
    cout<<"name = "<<name<<"   age = "<<age<<"   money = "<<money<< "   skill = "<<skill<<endl;
}
 
void Person::set_money(int m)
{
    money = m;
}
 
/***************************************上面是person的结构体的内容*************************************************/
struct Student : Person         //定义一个学生结构体,继承自Person结构体
{
private:
    double score;        //学生类型扩充的私有成员
 
public:
    void study()
    {
        cout<<"good good study  day  day  up!!!!"<<"   I am studding "<<skill<<endl;
        //cout<<"money = "<<money<<endl;
    }
};
 
int main()
{
    //使用结构体类型定义变量
    //struct Person p1;
    Person p1;                //定义结构体变量时可以不用加struct
    p1.age = 20;
    p1.name = "zhangpp";
    p1.speaker();
    //cout<<p1.money<<endl;         //私有成员外部无法直接访问,需要在结构体中提供公共的接口来操作
    p1.set_money(111111111);
    p1.speaker();
    //cout<<p1.skill<<endl;       //受保护的属性在外部无法直接访问,但是,在结构体内和子结构体中可以访问
    cout<<"*******************************************************"<<endl;
 
    Student s1;         //定义一个学生类型的结构体变量
    s1.name = "zhangsan";
    s1.speaker();
    s1.study();
 
    return 0;
}

作业:

使用C++手动封装一个顺序表,包含成员数组一个,成员变量N个

#include <iostream>
using namespace std;

//类型重命名
using datatype = int;
#define MAX 30

struct SeqList
{
    private:
        datatype *data;                //顺序表的数组
        int size = 0;                   //数组的大小
        int len = 0;                    //顺序表实际长度

    public:
        //初始化函数
        void init(int s)
        {
            size = s;            //当前数组的最大容量
            data = new datatype[size];      //在堆区申请一个顺序表容器
        }

        //判空函数
        bool empty()
        {
            return len == 0;
        }

        //判满函数
        bool full()
        {
            return len == size;
        }

        //添加数据函数
        bool add(datatype e)
        {
            if (full())//如果满了,则扩充
            {
                expend();
            }
            data[len++] = e;
            return true;
        }

        //求当前顺序表的实际长度
        int length()
        {
            return len;
        }

        //任意位置插入函数
        bool insert_pos(int pos, datatype e)
        {
            if (pos < 0 || pos > len)
            {
                cout << "插入失败" << endl;
                return false;
            }
            if (full())
            {
                expend();
            }
            for (int i = len; i > pos; --i)
            {
                data[i] = data[i - 1];
            }
            data[pos] = e;
            len++;
            return true;
        }

        //任意位置删除函数
        bool delete_pos(int pos)
        {
            if (pos < 0 || pos >= len)
            {
                cout << "删除失败" << endl;
                return false;
            }
            for (int i = pos + 1; i < len; ++i)
            {
                data[i - 1] = data[i];
            }
            len--;
            return true;
        }

        //访问容器中任意一个元素 at
        datatype &at(int index)
        {
            if (index < 0 || index >= len)
            {
                 throw out_of_range("无法找到该位置的值");//抛出异常
            }else{
            return data[index];}
        }

        //君子函数:二倍扩容
        void expend()
        {
            int newSize = size * 2;
            datatype *newData = new datatype[newSize];
            for (int i = 0; i < size; ++i)
            {
                newData[i] = data[i];
            }
            delete[] data;
            data = newData;
            size = newSize;
        }
};
int main() {
    SeqList list;
    int choice;
    list.init(30); // 初始化顺序表,最大容量为10
    while(1)
    {
        cout << "请选择操作:" << endl;
                cout << "1、添加数据" << endl;
                cout << "2、插入数据" << endl;
                cout << "3、删除指定位置的数据" << endl;
                cout << "4、查看指定位置的数据" << endl;
                cout << "5、查看当前顺序表的实际长度" << endl;
                cout << "请输入>>>>>>>";
                cin >> choice;
                switch (choice) {
                    case 1: {
                        int value;
                        cout << "请输入要添加的数据:";
                        cin >> value;
                        list.add(value);
                        break;
                    }
                    case 2: {
                        int pos, value;
                        cout << "请输入要插入的位置和数据:";
                        cin >> pos >> value;
                        list.insert_pos(pos, value);
                        break;
                    }
                    case 3: {
                        int pos;
                        cout << "请输入要删除的位置:";
                        cin >> pos;
                        list.delete_pos(pos);
                        break;
                    }
                    case 4: {
                    try {
                        int pos;
                        cout << "请输入要查看的位置:";
                        cin >> pos;
                        list.at(pos);//判断是否有错,若有错则直接输出错误原因
                        cout << "位置" << pos << "的数据是:" << list.at(pos) << endl;
                    } catch (const out_of_range& e) {
                        cerr << "错误:" << e.what() << endl;//捕获异常
                    }
                        break;
                    }
                    case 5: {
                        cout << "当前顺序表的实际长度是:" << list.length() << endl;
                        break;
                    }
                    default: {
                        cout << "无效的选择,请重新输入。" << endl;
                        break;
                    }
                }
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值