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;
}

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

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

内存四区的意义

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

二、引用

在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、引用传递
来实现一下两个数的交换:

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;
}

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

在这里插入图片描述
我们传参的时候,就是将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;
}

在这里插入图片描述
我们可以知道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;
}

函数重载注意事项

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


 void fun()
 {
 cout << "fun()" << endl;
 }
 void fun(int n=0)
 {
 cout << "fun(int n=0)" << endl;
 }

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

void fun(int &b)
{
	cout << "fun(int &b)" << endl;
}
void fun(const int &b)
{
	cout << "fun(const int &b)" << endl;
}

这也满足函数重再的条件吧,一个为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;
	}//行为
};

在这里插入图片描述

访问权限

class默认访问权限是私有;

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

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

class和struct

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

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

  • 25
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 36
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 36
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南猿北者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值