C++核心编程(一)
C++ 核心編程
本系類列博客都是根據(jù)黑馬的C++視頻做的筆記。
本階段主要針對C++面向?qū)ο缶幊碳夹g(shù)做詳細記錄,探討C++中的核心和精髓。
1、內(nèi)存分區(qū)模型
C++程序在執(zhí)行時,將內(nèi)存大方向劃分為4個區(qū)域
- 代碼區(qū):存放函數(shù)體的二進制代碼,由操作系統(tǒng)進行管理
- 全局區(qū):存放全局變量和靜態(tài)變量以及常量
- 棧區(qū):由編譯器自動分配釋放,存放函數(shù)的參數(shù)值,局部變量等
- 堆區(qū):由程序員分配和釋放,若程序員不釋放,程序結(jié)束時由操作系統(tǒng)回收
內(nèi)存四區(qū)的意義
不同區(qū)域存放的數(shù)據(jù),賦予不同的生命周期,給我們更大的靈活編程
1.1 程序運行前
在程序編譯后,生成了exe可執(zhí)行程序,未執(zhí)行該程序前分為兩個區(qū)域:
- 代碼區(qū)
存放CPU執(zhí)行的機器指令
代碼區(qū)是共享的,共享的目的是對于頻繁被執(zhí)行的程序,只需要在內(nèi)存中有一份代碼即可
代碼區(qū)是只讀的,使其只讀的原因是防止程序意外地修改了它的指令 - 全局區(qū)
全局變量和靜態(tài)變量存放于此
全局區(qū)還包含了常量區(qū),字符串常量和其他常量也存放于此
該區(qū)域的數(shù)據(jù)在程序結(jié)束后由操作系統(tǒng)釋放
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()
{// 全局區(qū)// 全局變量、靜態(tài)變量、常量// 創(chuàng)建一個普通的局部變量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;// 靜態(tài)變量static int s_a = 10;static int s_b = 10;cout << "靜態(tài)變量s_a的地址為:" << (int)&s_a << endl;cout << "靜態(tài)變量s_b的地址為:" << (int)&s_b << endl;// 常量// 字符串常量cout << "字符串'Hello world'的地址為:" << (int)&("Hello world") << endl;// const修飾的變量// const 修飾的全局變量,const修飾的局部變量cout << "全部常量c_g_a的地址為:" << (int)&c_g_a << endl;cout << "全部常量c_g_b的地址為:" << (int)&c_g_b << endl;// 局部const int c_l_a = 10; // c const; g global l localconst int c_l_b = 10;cout << "局部常量c_l_a的地址為:" << (int)&c_l_a << endl;cout << "局部常量c_l_b的地址為:" << (int)&c_l_b << endl;return 0;}
總結(jié)
- C++中在程序運行之前分為
全局區(qū)和代碼區(qū) - 代碼區(qū)特點是
共享和只讀 - 全局區(qū)中存放
全局變量、靜態(tài)變量、常量 - 常量區(qū)中存放
const修飾的全局變量和字符串常量
1.2 程序運行后
1.2.1 棧區(qū)
由編譯器自動分配釋放,存放函數(shù)的參數(shù)值,局部變量等
注意事項:不要返回局部變量的地址,棧區(qū)開辟的數(shù)據(jù)由編譯器自動釋放
示例
#include <iostream>using namespace std;// 棧區(qū)數(shù)據(jù)注意事項,不要反悔局部變量的地址
// 棧區(qū)的數(shù)據(jù)由編譯器管理開辟和釋放int* func(int b) // 形參數(shù)據(jù)也會保留在棧區(qū)
{int a = 10; // 局部變量,存放在棧區(qū),棧區(qū)的數(shù)據(jù)在函數(shù)執(zhí)行完成之后自動釋放return &a; // 返回局部變量的地址
}int main()
{// 接受func函數(shù)的返回值int* p = func(2);cout << *p << endl; // 本次可能打印正確的數(shù)據(jù),編譯器做了保留cout << *p << endl; // 第二次這個數(shù)據(jù)就不再保留了return 0;
}
1.2.3 堆區(qū)
- 由程序員分配釋放,若程序員不釋放,程序結(jié)束時由操作系統(tǒng)回收
- 在c++中主要利用
new在堆去開辟內(nèi)存
示例
#include <iostream>
using namespace std;int* func()
{// 利用new關(guān)鍵字可以將數(shù)據(jù)開辟到堆區(qū)// new 關(guān)鍵字返回的是地址// 指針本質(zhì)也是一個變量(局部變量),放在棧上,指針指向的數(shù)據(jù)是放在堆區(qū)int *a = new int(10);return a;
}int main()
{// 在堆區(qū)中開辟數(shù)據(jù)int* p = func();cout << *p << endl;cout << *p << endl;cout << *p << endl;return 0;
}
總結(jié)
堆區(qū)的數(shù)據(jù)由程序員管理開辟和釋放
堆區(qū)的數(shù)據(jù)利用關(guān)鍵字new進行開辟內(nèi)存
1.3 new操作符
C++中利用new操作符在堆區(qū)開辟數(shù)據(jù)
堆區(qū)開辟的數(shù)據(jù),有程序員手動開辟,手動釋放利用操作符delete
語法:new 數(shù)據(jù)類型
利用new創(chuàng)建的數(shù)據(jù)會返回對應(yīng)數(shù)據(jù)類型的指針
示例
#include <iostream>
using namespace std;int* func()
{// 在堆區(qū)創(chuàng)建整型數(shù)據(jù)// new 返回的是 該數(shù)據(jù)類型的指針int* p = new int(10);return p;
}void test01()
{int* p = func();cout << *p << endl;cout << *p << endl;// 堆區(qū)的數(shù)據(jù)由程序員管理,包括開辟和釋放// 釋放堆區(qū)中的數(shù)據(jù),利用關(guān)鍵字newdelete p;//cout << *p << endl; // 內(nèi)存已經(jīng)被釋放,再次訪問就是非法操作,就會報錯
}// 2、在堆區(qū)利用new開辟一個數(shù)組
void test02()
{// 創(chuàng)建10整型的數(shù)據(jù)的數(shù)組,在堆區(qū)int* arr = new int[10]; // 10代表數(shù)組里面有10個元素for (int i = 0; i <10; i++){arr[i] = i + 1;}for (int j= 0; j < 10; j++){cout << arr[j] << endl;}// 釋放堆區(qū)數(shù)組// 釋放數(shù)組的傷害,要加 []才可以delete[] arr;}
int main()
{//test01();test02();return 0;
}
注意
數(shù)組的釋放需要加[]才可以,例如:delete[] arr
2、引用
2.1 引用的基本使用
**作用:**給變量取別名
**語法:**數(shù)據(jù)類型 &別名 = 原名
圖示:
示例
#include <iostream>using namespace std;int main()
{// 引用的基本語法// 數(shù)據(jù)類型 &別名 = 原名int a = 10;// 創(chuàng)建引用int& b = a;cout << "a = " << a << endl;cout << "b = " << b << endl;// 修改值b = 100;cout << "a = " << a << endl;cout << "b = " << b << endl;return 0;
}
2.2 引用注意事項
- 引用必須要初始化
- 引用一旦初始化就不可以更改
圖示
示例
#include <iostream>
using namespace std;int main()
{int a = 10;// 1. 引用必須初始化//int& b; // 錯誤,必須要初始化int& b = a;// 2、 引用在初始化后,不可以改變int c = 20;b = c; // 此為賦值操作,并不是更改引用cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;}
2.3 引用做函數(shù)的參數(shù)
作用: 函數(shù)傳參是,可以利用引用的技術(shù)讓形參修飾實參
優(yōu)點: 可以簡化指針修改實參
示例
#include<iostream>
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;cout << "a = " << a << endl;cout << "b = " << b << endl;// mySwap01(a, b); // 未交換// mySwap02(&a, &b); // 已經(jīng)交互mySwap03(a, b); // 已經(jīng)交換cout << "a = " << a << endl;cout << "b = " << b << endl;return 0;
}
總結(jié):通過引用參數(shù)產(chǎn)生的效果和按地址傳遞是一樣的。引用的語法更清楚簡單。
2.4 引用做函數(shù)的返回值
作用: 引用是I可以作為函數(shù)的返回值存在的
注意:不要返回局部變量引用
用法:函數(shù)調(diào)用作為左值
示例
#include<iostream>
using namespace std;// 引用做函數(shù)的返回值
// 1、不要反悔局部變量的引用
int& test01()
{int a = 10; // 局部變量存放在四區(qū)中的棧區(qū)return a;
}// 2、函數(shù)調(diào)用可以可以作為左值
int& test02()
{static int a = 10; // 靜態(tài)變量,存放在全局區(qū),在程序結(jié)束后釋放return a;
}int main()
{//int& ref = test01();//cout << "ref = " << ref << endl; // 第一次 正確的數(shù)據(jù) 編譯器做了保留//cout << "ref = " << ref << endl; // 第二次 錯誤結(jié)果 a的內(nèi)存已經(jīng)釋放 無權(quán)操作int& ref2 = test02();cout << "ref2 = " << ref2 << endl;cout << "ref2 = " << ref2 << endl;// 左值test02() = 1000;cout << "ref2 = " << ref2 << endl;cout << "ref2 = " << ref2 << endl;return 0;
}
2.5 引用的本質(zhì)
本質(zhì): 引用的本質(zhì)在c++中就是一個指針常量
講解示例:
// 發(fā)現(xiàn)是引用,轉(zhuǎn)換為 int* const ref = &a;
void func(int& ref)
{ref = 200;
}int main()
{int a = 10;// 自動轉(zhuǎn)換為 int* const ref = &a;指針常量是指針指向不可更改int& ref = a;ref = 20; // 內(nèi)部發(fā)現(xiàn)ref是引用,自動幫我們轉(zhuǎn)換為 *ref = 20;cout << "a = " << a << endl;cout << "ref = " << ref << endl;func(a);return 0;}
圖示
結(jié)論:C++推薦使用引用計數(shù),因為語法方便,引用本質(zhì)就是指針常量,但是所有的指針操作編譯器都幫我們做了。
2.6 常量引用
作用: 主要用于修飾形參,防止誤操作
在函數(shù)形參列表中,可以加const修飾形參,防止形參改變實參
示例:
// 打印數(shù)據(jù)
void showValue(const int& val)
{// val = 10000; // 防止被修改cout << "val = " << val << endl;
}int main()
{// 常量引用// 使用場景:用來修飾形參,防止誤操作int a = 10;// 加上const之后,編譯器將代碼修改為 int temp = 10; const int& ref = temp;// 如果沒有const 也即:int& ref = 10; 為錯誤語法const int& ref = 10; // 引用必須引用一塊合法的內(nèi)存空間// ref = 20; // 加入const之后變?yōu)橹蛔x,不可寫showValue(a);return 0;
}
3、函數(shù)提高
3.1 函數(shù)默認參數(shù)
在C++中,函數(shù)的形參列表中的形參是可以有默認值得。
語法:返回值類型 函數(shù)名(參數(shù) = 默認值){}
示例:
#include<iostream>
using namespace std;// 函數(shù)默認參數(shù)
int sum(int a = 0, int b = 0, int c = 0)
{return a + b + c;
}// 注意事項:、
// 1、如果某個位置已經(jīng)有了默認參數(shù),那么這個位置之后,都必須要有默認值
// 2、如果函數(shù)聲明有默認參數(shù),函數(shù)實現(xiàn)就不能有默認參數(shù),如果函數(shù)實現(xiàn)有默認參數(shù),聲明就不能有參數(shù)int func(int a = 10, int b = 20, int c = 30);
int func(int a, int b, int c)
{return a + b + c;
}int main()
{cout << sum() << endl;cout << sum(10, 20) << endl;cout << sum(10, 20, 20) << endl;cout << func(20, 100) << endl;return 0;
}
注意: 如果某個位置有了默認參數(shù),后面的參數(shù)必須都要設(shè)定默認值,例如:int sum(int a = 0, int b)為錯誤使用。
注意: 如果函數(shù)聲明有默認參數(shù),函數(shù)實現(xiàn)就不能有默認參數(shù),如果函數(shù)實現(xiàn)有默認參數(shù),聲明就不能有參數(shù)
結(jié)論: 如果傳入?yún)?shù),就用傳入的值,如果沒有,就是用默認值
3.2 函數(shù)占位符
C++中函數(shù)的形參列表里面可以有占位參數(shù),用來占位,調(diào)用函數(shù)時必須填補該位置
語法: 返回值類型 函數(shù)名(數(shù)據(jù)類型){}
在現(xiàn)階段函數(shù)的占位參數(shù)存在意義不大,但是后面的課程中會用到該特性
示例:
// 占位參數(shù)
// 返回值類型 函數(shù)名(數(shù)據(jù)類型) {}// 目前階段的占位參數(shù) 還用不到,后面知識點中會用到
// 占位參數(shù)還可以有默認參數(shù)
void func(int a, int = 10)
{cout << "this is func " << endl;
}int main()
{// 如果不給第二個占位參數(shù)傳值,則報錯func(10, 10);return 0;
}
3.3 函數(shù)的重載
3.3.1 函數(shù)重載概述
作用: 函數(shù)名可以相同,提高復(fù)用性
函數(shù)重載滿足條件:
- 同一個作用域下
- 函數(shù)名相同
- 函數(shù)參數(shù)的類型不同、個數(shù)不同、或者順序不同
注意: 函數(shù)的返回值不可以作為函數(shù)重載的條件
示例:
#include<iostream>
using namespace std;// 函數(shù)重載
// 可以讓函數(shù)名相同,提高復(fù)用性// 函數(shù)重載條件
// 1、同一個作用域
// 2、同一個函數(shù)名
// 3、參數(shù)的類型、個數(shù)以及類型不同
void func()
{cout << "func 的調(diào)用!" << endl;
}void func(int a)
{cout << "func 的調(diào)用!!" << endl;
}void func(double a)
{cout << "func double 的調(diào)用!!" << endl;
}void func(int a, double b)
{cout << "func(int, double) 的調(diào)用!!" << endl;
}void func(double b, int a)
{cout << "func(double, a) 的調(diào)用!!!" << endl;
}// 函數(shù)的返回值不可以作為重載條件
int main()
{// 如果不給第二個占位參數(shù)傳值,則報錯func();func(20);func(2.1);func(10.2, 20);func(20, 10.2);return 0;
}
3.3.2 函數(shù)重載注意事項
- 引用作為重載條件
- 函數(shù)重載遇到函數(shù)默認參數(shù)
示例:
#include<iostream>
using namespace std;// 函數(shù)重載的注意事項
// 1、應(yīng)用作為重載的條件
void func(int &a) // int &a = 10; 不合法
{cout << "func(int &a) 的調(diào)用!" << endl;
}void func(const int &a) // const int &a = 10 合法
{cout << "func(const int& a) 的調(diào)用!" << endl;
}// 2、函數(shù)重載碰到默認參數(shù)
void func2(int a)
{cout << "func2(int a) 的調(diào)用!" << endl;
}// 默認參數(shù)會導(dǎo)致二義性,和上面的函數(shù)有沖突
//void func2(int a, int b = 20)
//{
// cout << "func2(int a = 2) 的調(diào)用!" << endl;
//}// 2、函數(shù)重載遇到默認參數(shù)
// 函數(shù)的返回值不可以作為重載條件
int main()
{// 如果不給第二個占位參數(shù)傳值,則報錯int a = 10;func(a); // 調(diào)用的是:func(int& a)函數(shù)func(10); // 調(diào)用的是:func(const int& a)函數(shù)func2(2);return 0;
}
注意: 函數(shù)形參有默認值時,可能會導(dǎo)致函數(shù)二義性,也即調(diào)用函數(shù)不知道該調(diào)用哪一個函數(shù),應(yīng)當盡量避免這種情況。
總結(jié)
以上是生活随笔為你收集整理的C++核心编程(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringBoot中实现quartz定
- 下一篇: C++核心编程(三)