本筆記主要來源於教程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;
}
最後歡迎大家訪問我的個人博客青蛙聽禪的博客