C++学习笔记(2)---内存分区、引用,函数提高---默认、占位、重载

本文详细介绍了C++中的内存分区模型,包括代码区、全局区、栈区和堆区的特点与应用,并深入探讨了C++的引用机制、函数的默认参数、占位参数及函数重载等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1 内存分区模型

2 C++的引用---变量起别名

3 函数高级---默认参数,占位参数,函数重载

1 内存分区模型 

1.1 内存四区---不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编辑。

C++程序在执行时,将内存大方向划分为4个区域:

1、代码区:函数体的二进制代码,注释、由操作系统进行管理的;

2、全局区:全局变量、静态变量、常量;

3、栈区:编译器自动分配释放,存放函数的参数值,局部变量等;

4、堆区:程序员分配和释放,若程序员不释放,程序结束时由操作系统回收;

1.2 程序运行前---编译后,生成.exe可执行程序,未执行该程序前分为以下:

1、代码区:存放CPU执行的机器指令,

代码区共享---对于频繁被执行的程序,只需要内存中有一份代码;

代码区只读---防止程序意外地修改指令

2、全局区---全局变量、静态变量

全局区包含常量区,字符串常量和其他const修饰的常量;该区域数据程序结束后由操作系统释放

c-const;g-global-全局;l-local-局部

1.3 staic和const区别

1、static---规定作用域和存储方式

(1)局部变量,static规定其为静态存储方式,每次调用的初始值为上一次调用的值,调用结束后存储空间不释放;
(2)全局变量,如果以文件划分作用域的话,此变量只在当前文件可见;对于static函数也是在当前模块内函数可见.

2、const---只读,只在声明中使用;

(1)const放在变量类型名前,说明这个变量的值是保持不变的,该变量必须在定义时初始化,初始化后对它进行的任何赋值都是非法的。

(2)假如变量是一个非常量变量,而指针或者引用的类型名前使用了const,那么,可以直接修改变量,不能通过指针或者引用修改变量。

1.4 程序运行后

1、栈区:由编译器自动分配释放,存放函数的参数值,局部变量等,

注意:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

2、堆区:程序员分配释放,程序结束时,由操作系统回收

在C++中主要利用new关键字在堆区开辟内存

int *p=new int(10);//利用new关键字,将数据开辟到堆区;指针:本质是局部变量,放在栈上,指针保存的数据是放在堆区。

1.5 new操作符

C++中new操作符在堆区开辟数据,堆区开辟的数据,程序员手动开辟,释放利用操作符delete

语法:new 数据类型;利用new创建的数据,会返回改数据对应的类型的指针

2 C++的引用---变量起别名

语法:数据类型 &别名=原名;

//练习1---new关键词的用法
//1、new操作符在堆区开辟数据,程序员手动开辟,释放利用操作符delete;
//2、利用new创建的数据,会返回改数据对应的类型的指针
int* func(){
    int *a=new int(10);
    return a;
}
int* test()//在堆区利用new开辟数据{
	int *arr=new int[10];//创建10整型数据的数据,在堆区,10代表数据由10个数据,int代表数据元素类型
	for(int i=0;i<10;i++){
		arr[i]=i+100;
	}
	for(int i=0;i<10;i++){
		cout<<arr[i]<<endl;
	}
	return arr;
}
int main(){
    int *p=test();
    cout<<*p<<endl;
	//delete p;//delete释放在堆区创建的指针
	delete []p;//释放数组时,需要加[]
	return 0;
}

2.1 引用的注意事项

(1)引用必须初始化;(2)引用在初始化后,不可以改变再变成其他变量的别名了

//练习2---C++引用
//作用:给变量起别名
//语法:数据类型 &别名=原名
//注意:引用必须初始化;初始化引用后,不可以更改
int main(){
	int a=10;
	int &b=a;
	cout<<"a="<<a<<endl;
	cout<<"b="<<b<<endl;

	b=20;
	cout<<"a="<<a<<endl;
	cout<<"b="<<b<<endl;

	int c=40;
	b=c;//赋值操作,没有更改引用
	
	cout<<"a="<<a<<endl;
	cout<<"b="<<b<<endl;
	cout<<"c="<<c<<endl;

	return 0;
}

2.2 引用做函数参数

(1)作用:函数传参时,可以利用引用的技术,让形参修饰实参;

(2)优点:可以简化指针修改实参

//引用作为函数参数,让形参修饰实参,可以简化指针修改实参
//值传递
void MySwap_0(int a,int b){
	int temp=a;
	a=b;
	b=temp;
	cout<<"MySwap_0_a="<<a<<endl;
	cout<<"MySwap_0_b="<<b<<endl;
	return;
}
//地址传递
void MySwap_1(int *a,int *b){
	int temp=*a;
	*a=*b;
	*b=temp;
	cout<<"MySwap_1_a="<<*a<<endl;
	cout<<"MySwap_1_b="<<*b<<endl;
	return;
}
//引用传递
void MySwap_2(int &a,int &b){
	int temp=a;
	a=b;
	b=temp;
	cout<<"MySwap_2_a="<<a<<endl;
	cout<<"MySwap_2_b="<<b<<endl;
	return;
}
int main(){
	int a=10;
	int b=20;
	//MySwap_0(a,b);
	//MySwap_1(&a,&b);
	MySwap_2(a,b);
	cout<<"a="<<a<<endl;
	cout<<"b="<<b<<endl;
	return 0;
}

2.3 引用做函数返回值

1、不要返回局部变量的引用;2、函数的调用作为左值

//引用做函数的返回值
//1、不要返回局部变量的引用
int& test01(){
	int a=1;//局部变量保存在四区中的栈区
	return a;
}
//2、函数的调用可以作为左值
int& test02(){
	static int a=10;//静态变量,存放在全局区,全局区的数据在程序结束系统进行释放
	return a;
}
//引用作为函数返回值
//注意:不要返回局部变量的引用
//用法:函数的调用作为左值
int main(){
	//int& ref=test01();
	//cout<<"ref="<<ref<<endl;//第一次输出ref=1,只是因为编译器作了保留
	//cout<<"ref="<<ref<<endl;//第二次输出ref=2039399392,a的内存已经释放

	int& ref=test02();
	cout<<"ref="<<ref<<endl;//ref=10;
	cout<<"ref="<<ref<<endl;//ref=10;
	
	//如果函数的返回值是引用,这个函数调用可以作为左值
	test02()=100;
	cout<<"ref="<<ref<<endl;//ref=100;
	cout<<"ref="<<ref<<endl;//ref=100;
	return 0;
}

2.4 引用的本质---C++内部实现是一个指针常量

//引用的本质---指针常量
int& ref=a;
//自动转换为int* const ref=&a;
//指针常量是指针指向不可改,也说明为什么引用不可更改

//发现是引用,转换为int* const ref=&a;
void func(int& ref){
    ref=100;//内部发现ref是引用,转换为*ref=100
}

2.5 常量引用---修饰形参,防止误操作

在函数形参列表中,可以加const修饰形参,防止形参改变实参

void ShowValue(const int& val){
    //val=100;//非法操作了,const规定只读,引用状态下可以修改实参,
    //通过const就可以防止修改产生误操作
    cout<<"val="<,val<<endl;
}

3 函数高级---默认参数,占位参数,函数重载

3.1 函数的默认参数

(1)如果自己传入数据,就用自己的数据,如果没有,用默认值;

(2)语法:返回值类型 函数名(形参=默认值){};

(3)如果某个位置有了默认值,从左至右位置都必须由默认值;

(4)如果函数声明有默认参数,函数实现就不能有默认参数;

int func(int a,int b=20,int c=30){
	return a+b+c;
}
int main(){
	cout<<func(10)<<endl;//输出60
	cout<<func(10,30)<<endl;//输出70
	return 0;
}

3.2 函数占位参数

C++中的函数的形参列表里可以有占位参数,调用函数时必须填补该位置;

占位参数也可以由默认参数

语法:返回值类型 函数名 (数据类型){}

void func(int a,int =10){
	cout<<"func"<<endl;
}

3.3 函数重载---函数名可以相同,提高复用性

1、函数重载满足条件

(1)同一作用域下;(2)函数名称相同;(3)函数参数类型不同||个数不同||顺序不同

 注意:函数的返回值不可以作为函数重载的条件

//(3)函数重载
void func(){
	cout<<"func的调用"<<endl;
}
void func(int a){
	cout<<"func(int a)的调用"<<endl;
}
void func(int a,double b){
	cout<<"func(int a,double b)的调用"<<endl;
}
void func(double b,int a){
	cout<<"func(double b,int a)的调用"<<endl;
}
int main(){
	func();
	func(20);
	func(20,3.14);
	func(3.14,20);
	return 0;
}
//注意!!函数的返回值不可以作为函数重载的条件
//以下函数不可以函数重载,会报错
//int func(double b,int a){
//	cout<<"func(double b,int a)的调用"<<endl;
//	return 0;
//}

2、函数重载的注意事项

1、引用作为重载条件;2、函数重载碰到函数默认参数---出现二义性,尽量避免

//1、引用作为函数重载
void func(int& a){//如果Int& a =10;引用的合法的空间,在栈上或者堆区,这个是不合法的
	cout<<"func(int& a)的调用"<<endl;
}
void func(const int& a){//只读,开辟在全局区
	cout<<"func(const)的调用"<<endl;
}
int main(){
	//int a=10;//局部变量,开辟在栈区
	//func(a);//此时调用的是func(int& a)
	func(10);//此时调用的是func(const int& a)

	return 0;
}
//2、函数重载碰到默认参数
void func(int a){
	cout<<"func(int a)的调用"<<endl;
}
void func(int a,int b=10){
	cout<<"func(int a,int b=10)的调用"<<endl;
}
//此时调用func(10)会引起二义性,应该尽量避免这种情况
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值