C++ 面向对象(一)
1.内存分区模型
在C++程序执行时,内存被划分为四个主要区域:代码区、全局区、栈区和堆区。
这些区域分别存储不同类型的数据,并具有不同的生命周期和管理方式,使编程更加灵活。
C++在程序运行前分为全局区和代码区。
代码区
代码区存放程序的二进制代码(我们写的全部代码翻译后的二进制机器指令),代码区具有共享和只读的特点。
只读的目的是防止程序意外修改指令。
共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。
全局区
全局区存放全局变量、static静态变量和const常量的数据,不存放 局部变量和局部常量 。全局区的数据在程序结束后由操作系统释放。全局区还包含常量区,存放字符串常量和其他常量(const修饰的变量)。
#include<iostream>
using namespace std;
// 全局变量
int g_a = 10;
int g_b = 10;
// const修饰的全局变量
const int c_g_a = 10;
const int c_g_b = 10;
int main() {
// 局部变量
int a = 10;
int b = 10;
cout << "局部变量a的地址为:" << (int)&a << endl;
cout << "局部变量b的地址为:" << (int)&b << endl;
// 全局变量
cout << "全局变量g_a的地址为:" << (int)&g_a << endl;
cout << "全局变量g_b的地址为:" << (int)&g_b << endl;
// 静态变量
static int s_a = 10;
static int s_b = 10;
cout << "静态变量s_a的地址为:" << (int)&s_a << endl;
cout << "静态变量s_b的地址为:" << (int)&s_b << endl;
// 字符串常量
cout << "字符串常量Hello World的地址为:" << (int)&"Hello World" << endl;
// const修饰的全局变量
cout << "全局常量c_g_a的地址为:" << (int)&c_g_a << endl;
cout << "全局常量c_g_b的地址为:" << (int)&c_g_b << endl;
// const修饰的局部变量
const int c_l_a = 10;
const int c_l_b = 10;
cout << "局部常量c_l_a的地址为:" << (int)&c_l_a << endl;
cout << "局部常量c_l_b的地址为:" << (int)&c_l_b << endl;
system("pause");
return 0;
}

运行结果说明局部变量、局部常量与其他的地址差异明显,它们不在代码区内。
栈区
栈区由编译器自动分配和释放,主要存放函数的参数值和局部变量。栈区的数据在函数执行完后自动释放,因此不要返回局部变量的地址。如下图代码,当执行输出后,第二次输出时数据就已经释放了,所以会输出一个乱码。
#include<iostream>
using namespace std;
int* func() {
int a = 10;
return &a; // 返回局部变量的地址
}
int main() {
int* p = func();
cout << *p << endl; // 第一次可以打印正确的数字
cout << *p << endl; // 第二次数据不再保留
system("pause");
return 0;
}
堆区
堆区由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。在C++中,主要利用new在堆区开辟内存。delete用于提前释放new的内存
而我们自己new的数据就会直到程序运行结束后才会释放。
注:指针本质上也是局部变量,放在栈上。指针保存的数据放在堆区。
#include<iostream>
using namespace std;
int* func() {
//利用new关键字在堆区开辟内存
int* p = new int(10);
return p; //
}
int main() {
int* p = func();
cout << *p << endl;
cout << *p << endl;
//释放堆区内存
delete p;
cout << *p << endl;
system("pause");
return 0;
}
new运算符
创建整型:new int( ) 创建数组:new int[ ]
#include<iostream>
using namespace std;
//1.new的基本语法
int* func() {
//在堆区创建整型数据
//new返回是 该数据类型的指针
int* p = new int(10);
return p; //
}
void test01()
{
int* p = func();
cout << *p << endl;
delete p;
cout << *p << endl;
}
//2、在堆区利用new开辟数组
void test2()
{
//创建10整型数据的数组,在堆区
int* arr = new int[10];
for (int i = 0; i < 10; i++)
{
arr[i] = i + 100;
}
for (int i = 0; i < 10; i++)
{
arr[i] = i + 100;
}
//释放堆区的数组
//如果不加[],只会释放一个数据
delete[]arr;
}
2.引用
2.1基本使用
作用:给变量起别名
语法:数据类型 &别名=原名
#include<iostream>
using namespace std;
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;
system("pause");
return 0;
}
/*输出结果:
a=10
b=10
a=20
b=20
*/
2.2 注意事项
1.引用必须初始化(int &b 不可以,int &b=a正确)
2.初始化后就不可以再当其他变量的别名了
2.3 引用做函数参数
作用:函数传参时,可以利用 引用 让形参修饰实参,简化指针修改实参。
#include<iostream>
using namespace std;
//交换函数
//01 值传递
void swap1(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
//02 地址传递
void swap1(int*a, int*b)
{
int temp = *a;
*a = *b;
*b = temp;
}
//03 引用传递
void swap3(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a = 1;
int b = 2;
swap3(a, b);
system("pause");
return 0;
}
引用传递相当于把主函数里的a和b放到引用函数中进行交换。
2.4 引用做函数返回值
X64代码无错误,X86局部变量作为返回值的函数只能输出一回,之后就被释放。
#include<iostream>
using namespace std;
//局部变量作为返回值
int& arr()
{
int a = 10;
return a;
}
//静态变量作为返回值
int& arr2()
{
static int a = 10;
return a;
}
int main()
{
int& a = arr();
cout << a << endl;
cout << a << endl;
int& b = arr2();
cout << b << endl;
cout << b << endl;
arr2() = 1000;
cout << arr2() << endl;
cout << arr2() << endl;
system("pause");
return 0;
}
2.5 引用的本质
本质:在C++内部实现是一个指针常量(int* const a)
#include<iostream>
using namespace std;
void func(int& ref)
{
ref = 100;//ref是引用,转换为*a=100
}
int main()
{
int a = 10;
//自动转换为 int* const ref = &a;指针常量是指指针指向不可更改,也说明为什么引用不可更改
int& ref = a;
ref = 10;//内部发现ref是引用,自动帮我们转换为:*ref=10;
cout << ref << endl;
cout << a << endl;
func(a);
cout << ref << endl;
cout << a << endl;
return 0;
}
结论:C++推荐使用引用技术,因为语法方便,引用本质是指针常量
2.6 常量引用
const int& 就相当于 const const int*,地址唯一且值唯一
#include<iostream>
using namespace std;
//引用使用的场景,通常用来修饰形参
void func(const int& ref)
{
cout << ref << endl;
}
int main()
{
//int& a=10;错误 引用本身需要一个合法的内存空间。
//加入const就可以了,编译器优化代码,int temp=10;const
const int& a = 10;
//加入const后不可以修改变量
cout << a << endl;
//函数中利用常量引用防止误操作修改实参
int a = 10;
func(a);
return 0;
}
3 函数高级
3.1 函数的默认参数
在C++中,函数的形参是可以有默认值的。
语法:返回类型 函数名 (参数=默认值)
int func2(int a, int b=10, int c=20)
{
return a + b + c;
}
//返回 40
func2(10);
//返回50
func2(10,20);
如果某个位置参数有默认值,那么这个位置往后的参数都应该有默认值
下图代码是错误的
int func3(int a, int b=10, int c)
{
return a + b + c;
}
如果函数声明有默认参数,函数实现就不能有默认参数
int func(int a,int b);
int func(int a=10,int b=10)
{
}
//或者
int func(int a=10,int b=10);
int func(int a,int b)
{
}
3.2 函数的占位参数
C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
语法:返回值类型 函数名 (数据类型){}
现阶段占位参数存在意义不大
void func(int a,int){}
func(10,10);
占位参数还可以用到默认参数
void func(int a,int =10){}
func(10);
3.3 函数重载
作用:函数名可以相同,提高复用性。
需要的条件:1.在同一个作用域
2.名称相同
3.函数参数类型不同或个数不同或顺序不同
注意:函数的返回值不可以作为函数重载的条件
3.4 函数重载的注意事项
1.引用作为函数重载的条件
int func(int& a)
{
return a;
}
int func(const int& a)
{
return a;
}
int main()
{
int a = 10;
// int& a=a;
func(a);
// const int& a=10
func(20);
}
2.函数重载遇到函数默认参数
int func(int a){}
int func(int a,int b=10){}
//此时会报错,因为这两个函数都适用,会出现二义性
func(10);
//这个就不会报错了
func(10,20);
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)