【黑马程序员 C++教程从0到1入门编程】【笔记3】C++核心编程(内存分区模型、引用、函数提高)
黑馬程序員匠心之作|C++教程從0到1入門編程,學習編程不再難
文章目錄
- 1 內存分區模型
- 1.1 程序運行前
- 1.2 程序運行后(手動開辟內存:c語言malloc,c++new)
- 1.3 new操作符(在堆區開辟數據)(delete釋放內存)(釋放數組delete后加[]:寫成`delete[] arr`)(利用new創建的數據,會返回該數據對應的類型的指針)
- 2 引用
- 2.1 引用的基本使用(給變量起別名)(語法:int& b = a;)(對引用的操作相當于對被引用變量的操作)
- 2.2 引用注意事項
- 2.3 引用做函數參數
- 2.4 引用做函數返回值(簡單解釋了引用的原理)(本質是指針常量)
- 2.6 常量引用(在函數形參列表中,可以加==const修飾形參==,防止形參改變實參)
- 傳常量引用,既有能獲取其被引變量地址的功能,又有防止誤修改的功能
- 3 函數提高
- 3.1 函數默認參數(只能見招拆招了,但是又怕那種報錯信息不全的,,。。)
- 3.2 函數占位參數(暫時不知道有什么用?)
- 3.3 函數重載
- 3.3.1 函數重載概述(條件:同一個作用域下、函數名稱相同、函數參數**類型不同** 或者 **個數不同** 或者 **順序不同**)
- 3.3.2 函數重載注意事項(引用作為重載條件【有的有優先級】、函數重載碰到函數默認參數【需要避免歧義】)
本階段主要針對C++ 面向對象編程技術做詳細講解,探討C++中的核心和精髓。
1 內存分區模型
C++程序在執行時,將內存大方向劃分為4個區域
- 代碼區:存放函數體的二進制代碼,由操作系統進行管理的
- 全局區:存放全局變量和靜態變量以及常量
- 棧區:由編譯器自動分配釋放, 存放函數的參數值,局部變量等
- 堆區:由程序員分配和釋放,若程序員不釋放,程序結束時由操作系統回收
內存四區意義:
不同區域存放的數據,賦予不同的生命周期, 給我們更大的靈活編程
1.1 程序運行前
? 在程序編譯后,生成了exe可執行程序,未執行該程序前分為兩個區域
? 代碼區:
? 存放 CPU 執行的機器指令
? 代碼區是共享的,共享的目的是對于頻繁被執行的程序,只需要在內存中有一份代碼即可
? 代碼區是只讀的,使其只讀的原因是防止程序意外地修改了它的指令
? 全局區:
? 全局變量和靜態變量存放在此.
? 全局區還包含了常量區, 字符串常量和其他常量也存放在此.
? 該區域的數據在程序結束后由操作系統釋放.
示例:
#include<iostream> #include<string> using namespace std;//全局變量 int g_a = 10; int g_b = 10;//全局常量 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 << "字符串常量地址為: " << (int)&"hello world" << endl;cout << "字符串常量地址為: " << (int)&"hello world1" << endl;cout << "全局常量c_g_a地址為: " << (int)&c_g_a << endl;cout << "全局常量c_g_b地址為: " << (int)&c_g_b << endl;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; }運行結果:
局部變量a地址為: 9435484 局部變量b地址為: 9435472 全局變量g_a地址為: 10731520 全局變量g_b地址為: 10731524 靜態變量s_a地址為: 10731528 靜態變量s_b地址為: 10731532 字符串常量地址為: 10722292 字符串常量地址為: 10722308 全局常量c_g_a地址為: 10722096 全局常量c_g_b地址為: 10722100 局部常量c_l_a地址為: 9435460 局部常量c_l_b地址為: 9435448
總結:
- C++中在程序運行前分為全局區和代碼區
- 代碼區特點是共享和只讀
- 全局區中存放全局變量、靜態變量、常量
- 常量區中存放 const修飾的全局常量 和 字符串常量
1.2 程序運行后(手動開辟內存:c語言malloc,c++new)
? 棧區:
? 由編譯器自動分配釋放, 存放函數的參數值,局部變量等
? 注意事項:不要返回局部變量的地址,棧區開辟的數據由編譯器自動釋放
示例:
#include<iostream> #include<string> using namespace std;int* func() {int a = 10;return &a; }int main() {int* p = func();cout << *p << endl; //10 cout << *p << endl; //2057673096 //地址被自動釋放了system("pause");return 0; }? 堆區:
? 由程序員分配釋放,若程序員不釋放,程序結束時由操作系統回收
? 在C++中主要利用new在堆區開辟內存
示例:
#include<iostream> #include<string> using namespace std;int* func() {int* a = new int(10);return a; }int main() {int* p = func();cout << *p << endl; //10cout << *p << endl; //10system("pause");return 0; }總結:
堆區數據由程序員管理開辟和釋放
堆區數據利用new關鍵字進行開辟內存
1.3 new操作符(在堆區開辟數據)(delete釋放內存)(釋放數組delete后加[]:寫成delete[] arr)(利用new創建的數據,會返回該數據對應的類型的指針)
? C++中利用new操作符在堆區開辟數據
? 堆區開辟的數據,由程序員手動開辟,手動釋放,釋放利用操作符 delete
? 語法:new 數據類型
? 利用new創建的數據,會返回該數據對應的類型的指針
示例1: 基本語法
#include<iostream> #include<string> using namespace std;int* func() {int* a = new int(10);return a; }int main() {int* p = func();cout << *p << endl; //10cout << *p << endl; //10//利用delete釋放堆區數據delete p;//cout << *p << endl; //報錯,釋放的空間不可訪問system("pause");return 0; }示例2:開辟數組
#include<iostream> #include<string> using namespace std;//堆區開辟數組 int main() {int* arr = new int[10];for (int i = 0; i < 10; i++){arr[i] = i + 100;}for (int i = 0; i < 10; i++){cout << arr[i] << endl;}//釋放數組 delete 后加 []delete[] arr;system("pause");return 0; }運行結果:
100 101 102 103 104 105 106 107 108 1092 引用
2.1 引用的基本使用(給變量起別名)(語法:int& b = a;)(對引用的操作相當于對被引用變量的操作)
作用: 給變量起別名
語法: 數據類型 &別名 = 原名
示例:
#include<iostream> #include<string> using namespace std;int main() {int a = 10;int& b = a; //b的內存地址跟a的內存地址相同cout << "a = " << a << endl; //a = 10cout << "b = " << b << endl; //a = 10b = 100;cout << "a = " << a << endl; //b = 100cout << "b = " << b << endl; //b = 100system("pause");return 0; }2.2 引用注意事項
- 引用必須初始化
- 引用在初始化后,不可以改變
示例:
(更改引用的值,被引用的變量的值也會跟著改變,因為它們是同一個內存地址)
2.3 引用做函數參數
作用:函數傳參時,可以利用引用的技術讓形參修飾實參
優點:可以簡化指針修改實參
示例:
#include<iostream> #include<string> using namespace std;//1. 值傳遞 void mySwap01(int a, int b) {int temp = a;a = b;b = temp; }//2. 地址傳遞 void mySwap02(int* a, int* b) {int temp = *a;*a = *b;*b = temp; }//3. 引用傳遞 void mySwap03(int& a, int& b) {int temp = a;a = b;b = temp; }int main() {int a = 10;int b = 20;mySwap01(a, b);cout << "a:" << a << " b:" << b << endl; //a:10 b:20a = 10;b = 20;mySwap02(&a, &b);cout << "a:" << a << " b:" << b << endl; //a:20 b:10a = 10;b = 20;mySwap03(a, b); cout << "a:" << a << " b:" << b << endl; //a:20 b:10system("pause");return 0; }總結:通過引用參數產生的效果同按地址傳遞是一樣的。引用的語法更清楚簡單
(Dontla:我沒覺得簡單!!!!還引用了新的語法。。。。)
2.4 引用做函數返回值(簡單解釋了引用的原理)(本質是指針常量)
作用:引用是可以作為函數的返回值存在的
注意:不要返回局部變量引用
用法:函數調用作為左值
示例:
#include<iostream> #include<string> using namespace std;//發現是引用,轉換為 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; //20cout << "ref:" << ref << endl; //20func(a);cout << "a:" << a << endl; //100cout << "ref:" << ref << endl; //100return 0; }結論:C++推薦用引用技術,因為語法方便,引用本質是指針常量,但是所有的指針操作編譯器都幫我們做了
2.6 常量引用(在函數形參列表中,可以加const修飾形參,防止形參改變實參)
作用:常量引用主要用來修飾形參,防止誤操作
在函數形參列表中,可以加const修飾形參,防止形參改變實參
示例:
#include<iostream> #include<string> using namespace std;//引用使用的場景,通常用來修飾形參 void showValue(const int& v) {//v += 10;cout << v << endl; //10 }int main() {//int& ref = 10; 引用本身需要一個合法的內存空間,因此這行錯誤//加入const就可以了,編譯器優化代碼,int temp = 10; const int& ref = temp;const int& ref = 10;//ref = 100; //加入const后不可以修改變量cout << ref << endl; //10//函數中利用常量引用防止誤操作修改實參int a = 10;showValue(a);system("pause");return 0; }傳常量引用,既有能獲取其被引變量地址的功能,又有防止誤修改的功能
如果要修改,可采用這個辦法
#include<iostream> #include<string> using namespace std;int main() {int b = 10;const int& ref = b;//ref++; //不允許操作*((int*)(&ref)) = 0;printf("b = %d, ref = %d\n", b, ref); //b = 0, ref = 0system("pause");return 0; }3 函數提高
3.1 函數默認參數(只能見招拆招了,但是又怕那種報錯信息不全的,,。。)
在C++中,函數的形參列表中的形參是可以有默認值的。
語法:返回值類型 函數名 (參數= 默認值){}
示例:
#include<iostream> #include<string> using namespace std;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; }int main() {cout << "ret = " << func(20, 20) << endl; //50cout << "ret = " << func(100) << endl; //120cout << "ret = " << func2(100) << endl; //110system("pause");return 0; }3.2 函數占位參數(暫時不知道有什么用?)
C++中函數的形參列表里可以有占位參數,用來做占位,調用函數時必須填補該位置
語法: 返回值類型 函數名 (數據類型){}
在現階段函數的占位參數存在意義不大,但是后面的課程中會用到該技術
示例:
#include<iostream> #include<string> using namespace std;//函數占位參數 ,占位參數也可以有默認參數 void func(int a, int) {cout << "this is func" << endl; //this is func }int main() {func(10, 10); //占位參數必須填補system("pause");return 0; }3.3 函數重載
3.3.1 函數重載概述(條件:同一個作用域下、函數名稱相同、函數參數類型不同 或者 個數不同 或者 順序不同)
作用:函數名可以相同,提高復用性
函數重載滿足條件:
- 同一個作用域下
- 函數名稱相同
- 函數參數類型不同 或者 個數不同 或者 順序不同
注意: 函數的返回值不可以作為函數重載的條件(但是注意,如果參數的重載條件滿足,則返回值可以更改!!)
示例:
#include<iostream> #include<string> using namespace std;//函數重載需要函數都在同一個作用域下 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 func(double a, int b, int c) {cout << "func (double a ,int b)的調用!" << endl;return 0; }int main() {func();func(10);func(3.14);func(10, 3.14);func(3.14, 10);func(3.14, 10,20);system("pause");return 0; }3.3.2 函數重載注意事項(引用作為重載條件【有的有優先級】、函數重載碰到函數默認參數【需要避免歧義】)
- 引用作為重載條件
- 函數重載碰到函數默認參數
示例:
#include<iostream> #include<string> using namespace std;//函數重載注意事項 //1、引用作為重載條件void func(int& a) {cout << "func (int &a) 調用 " << endl; }void func(const int& a) {cout << "func (const int &a) 調用 " << endl; }//2、函數重載碰到函數默認參數void func2(int a, int b = 10) {cout << "func2(int a, int b = 10) 調用" << endl; }void func2(int a) {cout << "func2(int a) 調用" << endl; }int main() {int a = 10;func(a); //調用無constfunc(10);//調用有const//func2(10); //碰到默認參數產生歧義,需要避免system("pause");return 0; }注意:調用func(a)時,func(int& a);和func(const int& a);都能被調用到,優先調用func(int& a);
總結
以上是生活随笔為你收集整理的【黑马程序员 C++教程从0到1入门编程】【笔记3】C++核心编程(内存分区模型、引用、函数提高)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++中双冒号(两个冒号)【::】的作用
- 下一篇: 【黑马程序员 C++教程从0到1入门编程