• c++ 内存分区模型 引用 函数提高


    c++ 核心编程

    ========================================================================

    1 内存分区模型

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

    • 代码区: 存放函数体的二进制代码,由操作系统进行管理
    • 全局区: 存放全局变量和静态变量以及常量
    • 栈区: 由编译器自动分配释放,存放函数的参数值,局部变量等
    • 堆区: 由程序员分配和释放,若程序员不释放,程序结束后由操作系统回收

    内存四区意义:

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

    1.1 程序运行前

    在程序编译后,生成了exe可执行程序, 未执行前分为两个区域

    代码区

    ​ 存放CPU执行的机器指令

    ​ 代码区是共享的,共享的目的是对频繁执行的程序,只需要在内存中有一份代码即可

    ​ 代码区是只读的,使其只读的原因是防止程序意外的修改了指令

    全局区

    全局变量和静态变量存放此处

    全局区还包含了常量区,字符常量区和其他常量区

    该区域的数据在程序结束后由操作系统释放

    1.2 程序运行后

    栈区:

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

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

    函数中的局部变量会在这个函数结束后被销毁,所以返回的指针是野指针,野指针的使用会带来不可预估的风险

    int * func(){
        int a = 10;
        return &a;
    }
    
    void main(){
        int *p = func)();
        cout << * p << endl; 		// 第一次没问题 编译器保留一次
        cout << * p << endl; 		// 第二次数据是乱的 不是10 野指针
    }
    

    堆区:

    由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

    在C++中利用 new 在堆区开辟一块内存

    int * func(){
        // 利用new关键字可以将数据开辟到堆区
        int * p = new int(10);		 // new int(数据)  他会返回一个地址编号
        // 指针本身也是局部变量 存在栈区,解释器会自动分配释放,但是他的值指向的是 堆区的地址,堆区是由我们自己管理的 所以指向不会变
        return p;		
    }
    
    
    void main(){
        int *p = func();
        cout << * p << endl; 		// 第一次没问题 编译器保留一次
        cout << * p << endl; 		// 第二次数据是乱的 不是10 野指针
    }
    

    1.3 new操作符

    C++中利用new操作符在堆区开辟数据

    堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符号 delete

    语法: new 数据类型

    利用new创建数据,会返回该数据对应类型的指针

    示例1:

    int * func(){
        int * p = new int(10);
        return p;		
    }
    
    
    void main(){
        int *p = func)();
        cout << * p << endl; 	
    }
    
    #include <iostream>
    using namespace std;
    
    int* func() {
    	int* arr = new int[10];
    
    	for (int i = 0; i < 10; i++)
    	{
    		arr[i] = i;
    	}
    
    	for (int i = 0; i < 10; i++)
    	{
    		cout << arr[i] << endl;
    	}
    	return arr;
    }
    
    void main() {
    	int* p = func();
    	cout << *p << endl;
    	cout << *p << endl;
    	delete[] p;	// 释放数组
    }
    

    2 引用

    2.1 引用的基本使用

    作用: 给变量起别名

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

    #include <iostream>
    using namespace std;
    
    void main() {
    	// 引用基础语法
    	// 数据类型 &别名 = 原名
    	int a = 10;
    	// 创建引用
    	int& b = a;
    	cout << a << endl;	// 10
    	cout << b << endl;  // 10
    	b = 20;
    	cout << a << endl;  // 20
    }
    

    2.2 引用的注意事项

    • 引用必须初始化
    • 引用初始化后,不可以改变
    int main(){
        int a = 10;
        int b = 20;
        // int &c;  错误!必须初始化!!!
        int &c = a;	// 一旦初始化,就不能修改
        c = b ; // 这是赋值操作,不是更改引用
    }
    

    2.3 引用做函数的参数

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

    优点: 简化指针修改实参

    示例:

    // 1.值传递
    void mySwap(int a,int b){
        int temp = a;
        a = b;
        b = temp;
    }
    
    // 2.地址传递
    void mySwap(int* a, int * b){
        int temp = *a;
        *a = *b;
        *b = temp;
    }
    
    // 3.引用传递
    void mySwap3(int &a,int &b){
        int temp = a;
        a=b;
        b=temp;
    }
    
    void main(){
        int a = 10;
        int b = 10;
        mySwap3(a,b);	// 打印发现引用传递也可以修改实参
    }
    

    2.4 引用做函数的返回值

    作用: 引用是可以作为函数的返回值存在的!

    注意: 不要返回局部变量引用

    用法: 函数调用作为左值

    // 局部变量不要做返回值
    int& mySwap(){		// int后加&
        int a = 10;
        return a;
    }
    
    void main(){
        int &ref = mySwap();	// int & xx = func();
        cout << ref << endl; // 编译器第一次做了保留
        cout << ref << endl; // 数据乱码 局部变量a存在栈区被清除了 b的指向是 a的地址所以也没有了
    }
    
    // 2. 函数的低调用可以放左值
    int& mySwap(){		// int后加&
        static int a = 10;		// 全局区 在系统结束后释放!!!
        return a;
    }
    
    void main(){
        int &ref = mySwap();
        cout << ref << endl; // 编译器第一次做了保留
        mySwap() = 1000;  // 返回的是一个a变量的引用
        // 如果函数的返回值是引用!这个函数可以作为左值!!
        cout << ref << endl; // 1000   // ref 2 只是一个a的别名! 数据一直存在一个内存
        cout << ref << endl; // 1000
    }
    

    2.5 引用的本质

    本质: 引用的本质在c++内部实现是一个指针常量

    讲解示例:

    // 发现引用转换为 int * const ref = &a;  值能修改 ,指向不能修改 指针常量
    void func(int &ref){
        ref = 100;	// ref 是引用, 转为 *ref = 100
    }
    
    int main(){
    	int a = 10;
        // 转换为 int * const ref = &a;  值能修改 ,指向不能修改 指针常量
        int & ref = a;
        ref = 20; // *ref = 20;
    }
    

    2.6 常量引用

    作用: 常量引用主要用来修饰形参,防止误操作

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

    void ShowValue(const int&v){
        cout << v << endl;
    }
    
    const int & ref = 10; // 这样可以
    int & ref = 10; // 报错 引用必须引用一块合法的内存空间
    

    3 函数提高

    3.1 函数默认参数

    在C++,函数的形参列表中的形参是可以有默认值得

    语法:返回值列席 函数名(参数=默认值){}

    int func2(int a,int b);
    int func(int a, int b=10,int c= 10){
        return a+b+c;
    }
    
    // 1.如果某个位置参数有默认值,那么从这个位置往后,必须都要有默认值
    // 2.如果函数声明有默认值,函数实现的时候就不能有默认值
    int func2(int a= 10,int b=10);
    int func2(int a,int b){
        return a+b;
    }
    

    3.2 函数占位参数

    C++中函数的形参列表里面可以有占位参数,用来做占位符,调用函数时必须填补改位置

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

    // 函数占位参数,占位参数也可以有默认值
    void func(int a,int){
        cout << "this is func" << endl;
    }
    
    int main(){
        func(10,10); // 占位参数必须填补
    }
    

    3.3 函数重载

    3.3.1 函数重载概述

    作用: 函数名可以相同,提高复用性

    函数重载满足条件

    • 同个作用域下
    • 函数名相同
    • 函数形参类型不同, 或者 个数不同 获取顺序不同

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

    void func(int a,int) {
    	cout << a << endl;
    }
    
    void func(string a, int) {
    	cout << a << endl;
    }
    
    void func(string a,int b, int) {
    	cout << a << endl;
    }
    

    3.3.2 函数重载注意事项

    • 引用作为重载注意事项
    • 函数重载碰到默认参数
    void func(int & a) {   // int & a = 10 不合法
    	cout << a << endl;
    }
    
    void func(const int & a) {  // 优化了 创建一个临时空间 再赋值给 a  int temp = 10; const & a=temp
    	cout << a << endl;
    }
    
    void main(){
        func(10);  // 执行const int & a
    }
    
    void func(int a) {  
    	cout << a << endl;
    }
    
    void func(int a ,int b = 10) { 
    	cout << a << endl;
    }
    
    void main(){
        func(10);   // 报错了 上下都能调用 编译器不知道调用那个 劲量避免这种情况 
        func(10,20);	// 这个能编译通过
    }
    
  • 相关阅读:
    全屏后无法检查键值
    根据秒转化为周天时秒分,不足补0
    日期时间格式化(Date对象)
    AS3 RGB颜色
    Egret eui中eui.ItemRenderer类型的组件在渲染界面时,dataChanged()重复调用
    egret 画圆 画圆角矩形 画矩形
    egert 缓动
    Flashdeveloper开多个实例进程解决方案
    flash 发布exe文件
    sublime Text3 如何自动排版代码
  • 原文地址:https://www.cnblogs.com/lddragon/p/16047866.html
Copyright © 2020-2023  润新知