• C++基本语法(一)



    前言

    记录一下C++基本语法


    一、内存四区

    C++程序在执行时将内存分为四个区域;

    程序运行前:

    在程序编译后,生成了.exe文件,未执行该程序前分为两个区域:

    代码区

    1、存放cpu执行的机器指令;
    2、代码区是共享的,共享的目的是针对频繁被执行的程序,只需要在内存中存在一份即可;
    3、代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令;

    全局区

    1、全局变量和静态变量、const修饰的全局变量存放于此;
    2、全局区还包括:常量区、字符常量和其它常量也存在于此,该区域只可读;
    3、该区域的数据在程序执行玩过后,由操作系统进行释放;

    程序运行之后:

    栈区

    1、存放函数的参数、局部变量,由编译器自动分配释放;
    2、注意事项:不要放回局部变量的地址,栈区开辟的空间由编译器自动释放;
    在这里插入图片描述
    我们看一看运行结果:
    在这里插入图片描述
    明明是同一块空间,为什么访问出来的值确不一样了?
    主要是应为a的空间是在fun函数上开辟的,随着fun调用结束,fun函数栈帧也就销毁了,a又是依赖fun’函数的函数栈帧“存活”的,栈帧都没了,a的空间也就理应呗操作系统回收,不在属于你,你也就无权访问了;因此,我们在返回a的地址时,虽然能找到这块空间,但是如果我们硬是强行访问他,就会造成非法访问,编译器会报警告;当然编译器为了防止你的误操作,在我们返回栈上开辟的空间的地址的时候,我们暂时不会去修改这块空间的值,这也是我们第一次*p去访问a能得到的结果,但是第二次及后面的多次去访问就不行了,这是因为编译器认为你第一次已经访问了这块空间,已经把想要的数据拿到了,所在接下来他就会把这块空间的数据给清除了,拿去给操作系统使用;这也是为什么第二次打印的时候时乱码,也是为什么不要返回局部变量的地址的原因;

    堆区

    1、由程序员分配释放,若程序员不释放,则在程序结束后由操作系统来回收
    2、在C++中主要利用new这个关键字来向堆区申请空间;

    new的基本语法

    在C++中new的基本语法就是
    new+类型+(初始值);
    接下来我们来实战看看;
    在这里插入图片描述
    当然我们也可以用花括号的方式来初始化所开辟的空间:
    在这里插入图片描述

    当然这个初始值可要可不要;
    在这里插入图片描述
    如果你带了括号,而没有给括号里面给值,默认用0来初始化;
    如果你没有括号那一部分,那么就是随机值;

    在这里插入图片描述
    当然我们也可以用new来开辟一段连续的空间:
    在这里插入图片描述

    对于数组来说,赋值的话,只能用花括号来赋值,不能用圆括号;
    在这里插入图片描述
    不能用圆括号对数组进行赋值:
    在这里插入图片描述

    如果不赋值的话,就是随机值;
    在这里插入图片描述
    对于new所开辟的空间,我们不必再像C语言那样判断一下,返回的指针是不是空指针,因为如果开辟的空间太大了,编译器是会抛出异常报错的;
    在这里插入图片描述
    当然既然我们开辟了空间就得释放啊!
    C++给我们提供了delete关键字,用于释放所开辟的空间:
    语法:delete+地址
    如果开辟的是块连续的空间我们就得delete+[ ]+首地址;(告诉编译器我要释放的是一块连续的空间)
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    释放数组:
    在这里插入图片描述
    在这里插入图片描述
    既然了解了这四大区,接下来我们再从地址的方面了解一下这四大区:

    int g_a = 10;
    const int g_b = 20;
    int main()
    {
    	int a = 10;
    	const int b = 10;
    	int arr[10] = { 1 };
    	cout << "全局区:"<<endl;
    	cout << &g_a << endl;
    	cout << &g_b << endl;
    	cout << &"abcd" << endl;
    	cout << "栈区:" << endl;
    	cout << &a << endl;
    	cout << &b << endl;
    	cout << arr << endl;
    	cout << "堆区:"<<endl;
    	int* p1 = new int(1);
    	int *p2 = new int(2);
    	int *p3 = new int[10]{ 1 };
    	cout << p1 << endl;
    	cout << p2 << endl;
    	cout << p3 << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述
    在这里插入图片描述

    我们从地址上就可以看出来其实这几个区隔的比较远,通过多组结果我么发现,全局区的数据集中在一起,栈区的数据集中再一起,堆区的数据也是集中在一起的,我们发现这些数据的存储方式是那么的有顺序,这更加证明了这几个区的存在;

    内存四区的意义

    不同区域存放的数据,赋予不同的生命周期,给我们最大的灵活编程;

    二、引用

    在C++中存在引用这个东西,这是个什么东西呢,简单来说就是取别名:
    比如:
    在这里插入图片描述
    这块空间本名叫a但是我们还可以给这块空间取个别名叫做b,以后呢我们说b和a都是代表着同一块空间;
    引用的基本语法:
    类型+&+别名=原名;
    具体看看:
    在这里插入图片描述
    既然都是表示的同一块空间,那么我们自然也就能对这块空间进行修改:

    引用的注意事项

    1、引用必须赋初值;
    在这里插入图片描述
    2、所赋的初值必须是一块合法的空间,不能是常量;
    在这里插入图片描述
    3、类型必须一样
    在这里插入图片描述
    类型不一样编译器会报错;
    4、const 修饰的别名
    表示这块空间里面的值不能被修改,只能可读
    在这里插入图片描述
    5、别名一旦取号过后,就不能更改;
    别名只能表示一块空间,不能表示多块空间:
    在这里插入图片描述

    b我原本是a的别名,那么现在我又用b来表示c的别名,对不起,编译器不允许,语法也不支持;也就表面在同一个作用域下,同一个别名只能作用于一块空间,不能重复利用;
    这里也就谈到了引用的本质

    
    int a=10;
    int &b=a;//本质就是int *const b=&a//const限制了其指向不可被更改,这也就解释了为啥别名一旦取名成功就不能更改引用的对象了;
    const int &c=10;//本质就是int tmp=10;const int &c=tmp;//这些都是编译器在编译时自动做的一些转换;
    
    • 1
    • 2
    • 3
    • 4

    引用作为函数参数和返回值

    我们先分别用
    1、值传递
    2、址传递
    3、引用传递
    来实现一下两个数的交换:

    void Swap1(int a,int b)//值传递
    {
    	int tmp = a;
    	a = b;
    	b = tmp;
    }
    void Swap2(int*a,int*b)//址传递
    {
    	int tmp = *a;
    	*a = *b;
    	*b = tmp;
    }
    void Swap3(int&m,int&n)//引用传递
    {
    	int tmp = m;
    	m = n;
    	n = tmp;
    }
    int main()//引用作为参数
    {
    	int a = 10;
    	int b = 20;
    	cout << "Swap1交换之前a=" <<a<<"b=" <<b<< endl;
    	Swap1(a, b);
    	cout << "Swap1交换之后a=" << a << "b=" << b <<"\n" << endl;
    
    	a = 10, b = 20;
    	cout << "Swap2交换之前a=" << a << "b=" << b << endl;
    	Swap2(&a, &b);
    	cout << "Swap2交换之后a=" << a << "b=" << b <<"\n" << endl;
    	
    	a = 10, b = 20;
    	cout << "Swap3交换之前a=" << a << "b=" << b << endl;
    	Swap3(a, b);
    	cout << "Swap3交换之后a=" << a << "b=" << b <<"\n" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    在这里插入图片描述
    首先值传递和址传递没问题;
    我们重点来讨论一下引用传递;
    在这里插入图片描述

    在这里插入图片描述
    我们传参的时候,就是将a的别名取名为m,b的别名取为n这没问题吧,那么我们的m,n与a,b是不是表示的是同一块空间,我们对同一块空间进行操作自然也就会交换两个变量之间的值;

    引用作为函数返回值

    int& fun()
    {
    	static int b = 19;
    	return b;
    }
    int main()
    {
    	int& ret = fun();
    	cout << ret << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述
    我们可以知道b变量是在全局区上的,因为前面又static修饰,b是一个静态变量;
    生命周期也就是伴随程序消亡;
    引用做返回值,我们可以理解为返回的是改变量的名字;
    我们接收的时候既可以用引用的方式接收,也可以用int来接收,二者都没有错误;
    前者的意思就是将b取别名;后者是使用的b的内容;
    在这里插入图片描述
    当然既然是这样的话,我们的返回值,还可以做左值来操作:
    在这里插入图片描述

    三、函数提高

    函数默认参数

    在这里插入图片描述
    我们知道这是正常的函数调用;
    那么我们可不可以在调用Add函数的时候,不传递参数直接调用呢?
    这肯定是不行的;
    但是如果我们给了参数一个默认参数似乎就可以了;
    在这里插入图片描述
    其中给参数赋值的操作叫做函数默认参数,就是只要给定了默认参数,那么我们对应的参数,如果在没有传值的情况下,就会使用默认参数:
    在这里插入图片描述

    注意事项

    1、从哪里开始的默认参数,那么从左往右都必须拥有默认参数:
    在这里插入图片描述
    比如我们从z开始写的默认参数,那么从z开始从左往右的所有参数都必须写默认参数,否则编译器将会报错;
    2、函数声明和函数定义的时候,默认参数只能存在在其中一个;函数定义时有了默认参数,函数声明时就不能有默认参数;函数声明时有了默认参数,函数定义时就不能有默认参数;
    在这里插入图片描述
    或者:
    在这里插入图片描述

    错误写法:
    在这里插入图片描述
    因为我声明时候的默认参数和定义时候的默认参数不一样,就会产生歧义,编译器就会不知道按照那个默认参数来执行;就算我们声明和定义的默认参数一样,编译器还是会报错,就是为了从根上断绝这种错误的写法!!!声明和定义只能存在于其中一个!!!;

    函数占位参数

    故名思意就是占个位置使的:
    在这里插入图片描述
    我们传参的时候必须给这个位置传递一个参数,尽管这个参数没有用上,就是必须传,不传就给你报错;
    当然我们可以给这个占位参数给个默认参数:
    在这里插入图片描述
    似乎变得更没用了🐵🐵🐵,目前阶段的确是没用,随着后期学习我们再来开发其进一步功能!!!

    函数重载

    作用: 函数名可以相同,提高复用性;
    函数重载满足条件:
    1、同一个作用域下;
    2、函数名称相同;
    3、参数不同(参数类型不同、顺序不同、个数不同等)
    示例:

    int fun()//1
    {
    	cout << "fun()"<<endl;
    	return 1;
    }
    int fun(int a)//2
    {
    	cout << "fun(int a)" << endl;
    	return a;
    }
    int fun(int a,int b)//3
    {
    	cout << "fun(int a,int b)" << endl;
    	return b;
    }
    int fun(int a,double b)//4
    {
    	cout << "fun(int a,double b)" << endl;
    	return a;
    }
    int fun(double ,int a)//5
    {
    	cout << "fun(double b,int a)" << endl;
    	return a;
    }
    int main()
    {
    	fun(10,20);//调用函数3
    	fun(10);//调用函数2
    	fun(10,3.14);//调用函数4
    	fun();//调用函数1
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    函数重载注意事项

    注意在设计重载函数的时候,确保唯一性,避免引发歧义:
    比如

    
     void fun()
     {
     cout << "fun()" << endl;
     }
     void fun(int n=0)
     {
     cout << "fun(int n=0)" << endl;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    首先语法上没有错误,也满足函数重载的条件:
    那么如果我们调用fun( );实际上是会去调用那个呢,这样写的目的是无参,还是我想使用默认参数?这样就会引发歧义,编译器不知道执行那个,就会报错;
    但是如果你是这样调用fun(1);这样就明确了,我们会去调用下面一个函数,编译器也不会报错;
    在这里插入图片描述
    在这里插入图片描述
    我们在设计重载函数的时候,应该避免这样设计,尽量避免歧义的出现;
    参数为引用:

    void fun(int &b)
    {
    	cout << "fun(int &b)" << endl;
    }
    void fun(const int &b)
    {
    	cout << "fun(const int &b)" << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这也满足函数重再的条件吧,一个为int,一个为const int;
    在这里插入图片描述
    那我们在调用的时候,好像掉哪一个都可以,好像又引发歧义了,但是事实并不是如此;
    对于第一个fun函数接受的是可访问可修改的,而我们穿过去的也是可访问,可修改的;第二个是只可访问,不可修改,编译器觉得调用第一个更好一些,自然也就不会发生歧义,尽管我们可以调用第二个,但是在编译器看来,自然是第一个更全面,更好;
    在这里插入图片描述
    如果我们传的是一个常量?
    在这里插入图片描述
    自然也就调用第二个;为什么?
    int &b=10;是非法操作,自然不可能;
    const int &b=10;合法操作,自然可以;

    四、类和对象

    C++面向对象的三大特性:封装、继承、多态;
    C++认为万事万物皆为对象,对象上有其属性和行为;

    人可以作为对象,属性有姓名、年龄、身高、体重…行为有走、跑、跳、吃饭、唱歌…
    车也可以作为对象,属性有轮胎、方向盘、车灯…行为有载人、放音乐、放空调…

    具有相同性质的对象,我们可以抽象称为类,人属于人类,车属于车类;

    封装的意义

    封装是C+ +面向对象三大特性之一

    封装的意义:
    ●将属性和行为作为一个整体,表现生活中的事物
    ●将属性和行为加以权限控制
    封装意义一:
    在设计类的时候,属性和行为写在一-起, 表现事物
    语法:class 类名(访问权限:属性:行为)
    示例:
    设计一个学生类,并展示其姓名学号

    class students//一个类的完整形式:
    {
    public://权限(默认是私有)
    	string name;
    	string sex;
    	string num;
    	int age;
    	//属性
    	void showMessage()
    	{
    		cout << "姓名:" << name<< endl;
    		cout << "性别:" << sex << endl;
    		cout << "年龄:" << age << endl;
    		cout << "学号:" << num << endl;
    	}//行为
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    访问权限

    class默认访问权限是私有;

    public: 公共权限:类内可以访问,类外也可以访问;
    private: 私有权限:类内可以访问,类外不可以访问;
    protected: 保护权限:类内可以访问,类外不可以访问;

    虽然私有权限和公共权限似乎是相同的,但是随着我们深入的学习,我们会发现之间的区别的;
    在这里插入图片描述
    随着我们权限的设置,性别,年龄等属性我们再类外也就无法访问;

    class和struct

    其实从class的访问上我们可以看出和struct完全一样,那么struct可不可以用来表示类呢?
    当然可以;
    在这里插入图片描述

    那么岂不是于class没有任何区别了?
    那到不是,struct默认权限是公共;
    class默认权限是私有;
    在这里插入图片描述

  • 相关阅读:
    深入解析NPM:常用命令详解与实战示例
    torch.as_tensor()、torch.Tensor() 、 torch.tensor() 、transforms.ToTensor()的区别
    Dubbo框架基本使用
    趣学算法【第一章:算法之美】感悟(上)
    Day 01 web前端基础知识
    深入理解C++红黑树的底层实现及应用
    2024-06-08 Unity 编辑器开发之编辑器拓展9 —— EditorUtility
    效率工具之Arthas
    为什么IDEA不推荐你使用@Autowired ?
    FT232替代GP232RL USB-RS232转换器芯片国产化应用
  • 原文地址:https://blog.csdn.net/qq_62106937/article/details/126438153