C++學習筆記(五)(內存分區,引用,函數)

本筆記主要來源於教程https://www.bilibili.com/video/av41559729?p=1

C++核心編程

本階段主要針對C++面向對象編程技術做詳細講解,探討C++中的核心和精髓。

1 內存分區模型

C++程序在執行時,將內存大方向劃分爲4個區域

  • 代碼區:存放函數體的二進制代碼,由操作系統進行管理的
  • 全局區:存放全局變量和靜態變量以及常量
  • 棧區:由編譯器自動分配釋放,存放函數的參數值,局部變量等
  • 堆區:由程序員分配和釋放,若程序員不釋放,程序結束時由操作系統回收

內存四區意義:
不同區域存放的數據,賦予不同的聲明週期,給我們更大的靈活編程

1.1 程序運行前

在程序編譯後,生成了exe可執行程序,未執行該程序前分爲兩個區域
代碼區:
    存放CPU執行的機器指令
    代碼區是共享的,共享的目的是對於頻繁被執行的程序,只需在內存中有一份代碼即可
    代碼區是只讀的,使其只讀的原因是防止程序意外地修改了它的指令
全局區:
    全局變量和靜態變量存放在此
    全局區還包含了常量區,字符串常量和其他常量也存放在此
    該區域的數據在程序結束後由操作系統釋放

寫在函數體內的變量爲局部變量,在函數體外定義的變量爲全局變量,全局變量和局部變量的地址不在同一段內。
靜態變量: 在普通變量前面加static static int a=10;   同樣存放在全局區中

常量分爲字符串常量(在“ "中)const修飾的變量,const修飾的變量又分爲const修飾的全局變量(全局常量)和const修飾的局部變量(局部常量
全局變量,靜態變量,常量(字符串常量,const修飾的全局變量(全局常量))在全局區中

1.2 程序運行後

棧區:
    由編譯器自動分配釋放,存放函數的參數值(形參),局部變量
    注意事項:不要返回局部變量的地址,棧區開闢的數據由編譯器自動釋放。

堆區:
    由程序員分配釋放,若程序員不釋放,程序結束時由操作系統回收
    在C++中主要利用new在堆區開闢內存。

int *func()
{
     //利用new關鍵字,可以將數據開闢到堆區
    //指針本質也是局部變量,放在棧上(即地址存放在棧上),指針保存的數據是放在堆區(通過地址來訪問數據)
    int *p= new int(10);
    return p;
}

int main()
{  

    //在堆區開闢數據
    int *p = func();

    cout << *p << endl;
    system("pause");
    return 0;

}

1.3 new操作符

    C++中利用new操作符在堆區開闢數據
    堆區開闢的數據,由程序員手動開闢,手動釋放,釋放利用操作符delete
    語法 new 數據類型;
    利用new創建的數據,會返回數據對應的類型的指針

#include< iostream>
using namespace std;

  //new的基本語法
int *func()
{
    //在堆區創建整型數據
    //new返回的是該數據類型的指針
    int *p=new int(10);
    return p;
}
void test01()
{
    int*p = func();
    cout << *p << endl;
    cout << *p << endl;
    cout << *p << endl;
    //堆區的數據,由程序員管理開闢,程序員管理釋放
    //如果想釋放堆區的數據,用關鍵字delete
    delete p;
    cout << *p << endl;//會報錯
}

//2.在堆區利用new開闢數組
void test02()
{
    //創建10整型數據的數組
    int *arr=new int[10];//10代表數組有十個元素
    for (int i = 0; i < 10; i++)
    {
        arr[i] = i + 100;//給10個元素賦值100~109
    }
    for (int i = 0; i < 10; i++)
    {
        cout << arr[i] << endl;

    }
    //釋放堆區數組
    //釋放數組時,要加[]纔可以
    delete[]arr;
}

int main()
{  
    //test01();
    test02();
    system("pause");
    return 0;

}

2 引用

2.1 引用的基本使用

作用:給變量起別名
語法:數據類型 &別名=原名

    int a = 10;
    int &b = a;
    b = 100;
    cout << "a= "<< a << endl;
    cout << "b= "<< b << endl;

改變b的值a的值也會改變

2.2 引用注意事項

  • 引用必須要初始化  (int &b;是錯誤的)
  • 引用在初始化後,不可以改變   b作爲a的別名時就不可以做c的別名

2.3 引用做函數參數

作用:函數傳參時,可以利用引用的技術讓形參修飾實參
優點:可以簡化指針修改實參

void mySwap01(int a,int b)
{
    int temp = a;
    a = b;
    b = temp;
    cout << "Swap01a= " << a << endl;
    cout << "Swap01b= " << b << endl;

}
//2.地址傳遞
void mySwap02(int*a, int*b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
    cout << "Swap02a= " << *a << endl;
    cout << "Swap02b= " << *b << endl;
}

//3.引用傳遞
void mySwap03(int&a,int&b)
{
    int temp = a;
    a = b;
    b = temp;
    cout << "Swap03a= " << a << endl;
    cout << "Swap03b= " << b << endl;
}

int main()
{  
    int a = 10, b = 20;
    //mySwap01(a, b);//值傳遞,形參不會修飾實參
    //mySwap02(&a,&b);//地址傳遞形參會修飾實參
    mySwap03(a, b);//引用傳遞,形參會修飾實參

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


    system("pause");
    return 0;

}

2.4 引用做函數返回值

作用:引用是可以作爲函數的返回值存在的

注意:不要返回局部變量引用
用法:函數調用作爲左值

//引用做函數返回值
//1.不要返回局部變量的引用
int& test01()
{
    int a = 10;//局部變量 存放在四區中的 棧區
    return a;

}

//2.函數的調用可以作爲左值
int& test02()
{
    static int a = 10;//靜態變量,存放在 全局區,全局區上的數據在程序結束後系統釋放
    return a;
}
int main()
{  
    int &ref = test01();

    cout << "ref= " << ref << endl;//第一次結果正確是因爲編譯器做了保留
    cout << "ref= " << ref << endl;//第二次結果錯誤因爲a的內存已經釋放

    int &ref2 = test02();
    cout << "ref2= " << ref2 << endl;
    cout << "ref2= " << ref2 << endl;

    test02() = 1000;//如果函數的返回值是引用,這個函數的調用可以作爲左值

    cout << "ref2= " << ref2 << endl;
    cout << "ref2= " << ref2 << endl;

    system("pause");
    return 0;

}

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是引用,自動幫我們轉換爲*ref=20;

    cout << "a= " << a << endl;
    cout << "ref= " << ref << endl;

    func(a);
    cout << "a= " << a << endl;
    cout << "ref= " << ref << endl;


    system("pause");
    return 0;

}

結論:C++推薦用引用技術,因爲語法方便,引用本質是指針常量,但是所有的指針操作編譯器都幫我們做了

2.6 常量引用

作用:常量引用主要用來修飾形參,防止誤操作

在函數形參列表中,可以加const修飾形參,防止形參改變實參

//打印數據函數
void showValue( const int &val)
{
    //val = 1000; //加了const不允許修改,防止誤操作
    cout << "val= " << val << endl;
}
int main()
{  
    //常量引用
    //使用場景:用來修飾形參,防止誤操作
    //int &refs = 10;//會報錯,引用必須引用一塊合法的內存空間

    //加上const之後 編譯器將代碼修改 int temp=10; const int &ref=temp;
    //const int &ref = 10;
    //ref = 20;//報錯,加入const之後變爲只讀狀態,不可以修改


    int a = 100;
    showValue(a);
    cout << "a=" << a << endl;

    system("pause");
    return 0;

}

3 函數提高

3.1 函數默認參數

在C++中,函數的形參列表中的形參是可以有默認值的。
語法: 返回值類型 函數名 (參數=默認值){}

//函數默認參數
//如果我們自己傳入數據,就用自己的,如果沒有,那麼用默認值
//語法: 返回值類型  函數名稱(形參=默認值){}
int func(int a,int b=20,int c=30)
{
    return a + b + c;
}
//注意事項
//1.如果某個位置已經有了默認參數,那麼從這個位置往後,從左到右都必須有默認值

//2.如果函數聲明有默認參數,函數實現就不能有默認參數
//聲明和實現只能有一個默認參數
int func2(int a = 10, int b = 10);

int func2(int a=10 , int b=10 )//報錯,不能重定義默認參數
{
    return a + b;
}
int main()
{  
    //cout << func(10) << endl;
    cout << func2(10,10 )<< endl;
    system("pause");
    return 0;

}

3.2 函數佔位參數

C++中函數的形參列表裏可以有佔位參數,用來做佔位,調用函數時必須填補該位置

語法:返回值類型 函數名(數據類型){}

在現階段函數的佔位參數意義不大,但是後面的課程中會用到該技術

//佔位參數
//返回值類型 函數名(數據類型){}
//佔位參數,還可以有默認參數
void func(int a,int=10)
{
    cout << "this is func " << endl;
}

int main()
{  
    func(10);
    system("pause");
    return 0;

}

3.3 函數重載

3.3.1 函數重載描述

作用: 函數名可以相同,提高複用性

函數重載滿足條件:

  • 同一個作用域下
  • 函數名稱相同
  • 函數參數類型不同,或者個數不同或者順序不同

注意:函數的返回值不可以作爲函數重載的條件

//函數重載
//可以讓函數名相同,提高複用性


//函數重載的滿足條件
//1.同一個作用域下
//2.函數名稱相同
//3.函數參數類型不同,或者個數不同,或者順序不同
void func()
{
    cout << "func 的調用" << endl;
}

void func(int a)
{
    cout << "func(int a) 的調用" << endl;
}
void func(double a)
{
    cout << "func(double a) 的調用" << endl;
}
void func(int a,double b)
{
    cout << "func(int a,double b) 的調用" << endl;
}
void func(double a,int b)
{
    cout << "func(double a,int b) 的調用" << endl;
}

//注意事項:函數的返回值不可以作爲函數重載的條件
//int func(double a, int b)//報錯,無法重載
//{
//  cout << "func(double a,int b) 的調用" << endl;
//}
int main()
{  
    func();
    func(10);
    func(3.14);
    func(10, 3.14);
    func(3.14, 10);
    system("pause");
    return 0;

}

3.3.2 函數重載注意事項

  • 引用作爲重載條件
  • 函數重載碰到函數默認參數
//函數重載注意事項
//1.引用作爲重載的條件
void func(int &a)//int &a=10:不合法
{
    cout << "func(int &a) 調用" << endl;
}
void func(const int &a)//const int &a=10:合法
{
    cout << "func(const int &a) 調用" << endl;
}
//2.函數重載碰到默認參數
void func2(int a,int b=10)
{
    cout << "func2(int a,int b)的調用" << endl;
}
void func2(int a)
{
    cout << "func2(int a)的調用" << endl;
}
int main()
{  
    int a = 10;
    func(a);//調用無const
    func(10);//調用有const
    func2(10);//當函數重載碰到默認參數,會出現二義性,報錯,儘量避免這種情況


    system("pause");
    return 0;

}

 
最後歡迎大家訪問我的個人博客青蛙聽禪的博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章