網站首頁 編程語言 正文
一、C/C++內存區域劃分
1. 棧又叫堆棧--非靜態局部變量/函數參數/返回值等等,棧是向下增長的。
2. 內存映射段是高效的I/O映射方式,用于裝載一個共享的動態內存庫。用戶可使用系統接口創建共享共享內存,做進程間通信。
3. 堆用于程序運行時動態內存分配,堆是可以上增長的。
4. 數據段--存儲全局數據和靜態數據。
5. 代碼段--可執行的代碼/只讀常量。
二、常見變量存儲區域
int globalVar = 1;//全局變量中在靜態區
static int staticGlobalVar = 1;//靜態區
void Test()
{
static int staticVar = 1;//靜態區
int localVar = 1;//棧區
int num1[10] = { 1, 2, 3, 4 };//棧區
char char2[] = "abcd";//棧區,*char2在棧區
const char* pChar3 = "abcd";//指針在棧區,*pchar3在常量區
int* ptr1 = (int*)malloc(sizeof(int) * 4);//指針在棧區,*ptr1在堆區
int* ptr2 = (int*)calloc(4, sizeof(int));///棧區
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);//棧區
free(ptr1);
free(ptr3);
}
三、new和delete
1、new和delete的使用方式
int main()
{
int* p1 = new int;//在堆區申請一個int大小的空間,不會初始化
int* p2 = new int(0);//申請并初始化為0
delete p1;
delete p2;
int* p3 = new int[10];//在堆區申請一塊10個int大小的空間,未初始化
int* p4 = new int[10]{ 1,2,3,4 };//初始化為{1,2,3,4,0,0,0,0,0,0}
delete[] p3;
delete[] p4;
return 0;
}
注意:申請和釋放單個元素的空間,使用new和delete操作符,申請和釋放連續的空間,使用new[]和delete[],一定要匹配起來使用。
2、new、delete和malloc、free的區別
1、對于內置類型,沒有區別。
2、new和delete是C++的關鍵字/操作符,而malloc和free是C語言的庫函數。
3、對于自定義類型,相比于malloc和free,new和delete會額外調用類中的構造函數和析構函數。
4、malloc的返回值是void*,使用時需要強轉,new后邊跟的是空間的類型,所以new不需要強轉。
5、malloc失敗返回空指針,需要判空;new失敗拋異常,需要捕獲異常。
3、new的原理
new等于operator new()+構造函數。operator new()不是new運算符的重載,因為參數沒有自定義類型。它是一個庫里的全局函數。
void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
// try to allocate size bytes
void *p;
while ((p = malloc(size)) == 0)
if (_callnewh(size) == 0)
{
// report no memory
// 如果申請內存失敗了,這里會拋出bad_alloc 類型異常
static const std::bad_alloc nomem;
_RAISE(nomem);
}
return (p);
}
從底層代碼可以看出operator new()是對malloc的封裝,如果malloc失敗,將會拋出異常。
4、delete的原理
delete等于operator delete()+析構函數
//operator delete: 該函數最終是通過free來釋放空間的
void operator delete(void *pUserData) {
_CrtMemBlockHeader * pHead;
RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));
if (pUserData == NULL)
return;
_mlock(_HEAP_LOCK); /* block other threads */
__TRY
/* get a pointer to memory block header */
pHead = pHdr(pUserData);
/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
_free_dbg( pUserData, pHead->nBlockUse );//調用free()
__FINALLY
_munlock(_HEAP_LOCK); /* release other threads */
__END_TRY_FINALLY
return; }
//free的實現
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
從底層代碼可以看出operator delete()調用了free。
所以針對內置類型或無資源的類對象delete時,使用delete和free效果相同。但對于有資源需要釋放的對象時,直接使用free雖然釋放了對象的空間,但對象內部的資源還未被清理,導致內存泄漏!這種情況必須使用delete。
5、new T[N]原理
1、new T[N]調用operator new[]
2、operator new[]調用operator new完成N個對象空間的開辟。
3、調用N次構造函數完成N個對象的初始化。
6、delete[]原理
1、調用N次析構函數完成N個對象資源的清理工作。
2、調用operator delete[]
3、operator delete[]調用operator delete完成整段空間的釋放。
四、定位new
1、定位new的概念
對于一個類,我們可以顯式的去調用類的析構函數,但是不能顯式調用構造函數,那么使用定位new,就可以顯式調用類的構造函數,對一塊空間重新初始化。
2、定位new的使用格式
new (指針)類名或者new (指針) type(初始化列表)
int main()
{
Date d1;
new(&d1)Date;//new (指針)類名
Date* p = new Date[4]{ {2022,10,15},{2023,11,8} };
new(p)Date[4];//new (指針) type(初始化列表)
delete[] p;
return 0;
}
上述代碼一共調用了10次構造函數,經過定位new的處理,d1和p所代表的空間已經被重新初始化了。
3、定位new的使用場景
一般不會像上邊代碼一樣,對一塊已有對象數據的空間重新初始化。定位new表達式在實際中一般是配合內存池使用。因為內存池分配出的內存沒有初始化,對于自定義類型的對象,可以使用定位new對這些沒有被初始化的內存顯式調用類的構造函數初始化。
五、泛型編程
泛型編程:編寫與類型無關的通用代碼,是代碼復用的一種手段。模板是泛型編程的基礎。
模板分為函數模板和類模板
六、函數模板
1、函數模板的使用
template<typename T>
void Swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 10, b = 5;
double m = 2.3, n = 4.9;
Swap(a, b);
Swap(m, n);
return 0;
}
兩個Swap調用的不是模板,而是模板生成的實例化函數,像上述代碼中,模板會生成int和double類型的兩種實例化函數。
2、不同類型形參傳參時的處理
2.1傳參時強轉(對應形參需要const修飾)
template<typename T>
T Add(const T& a,const T& b)//const接收常性實參
{
return a + b;
}
int main()
{
int a = 10, b = 5;
double m = 2.3, n = 4.9;
Add(a, (int)m);//強轉,臨時變量傳參,具有常性
return 0;
}
使用強制類型轉換在推演的時候將形參轉換成同一類型。
2.2顯式實例化(傳參時隱式類型轉,對應形參需要const修飾)
template<typename T>
T Add(const T& a, const T& b)//需要使用const接收
{
return a + b;
}
int main()
{
int a = 10, b = 5;
double m = 2.3, n = 4.9;
Add<int>(a, m);//顯式實例化,m發生隱式類型轉換
return 0;
}
顯式實例化編譯器不再去推演T的類型,而是直接使用尖括號內的類型實例化對應函數。
2.3使用多個模板
template<typename T1,class T2>//可以寫typename也可以寫class
T1 Add(const T1& a, const T2& b)
{
return a + b;
}
int main()
{
int a = 10, b = 5;
double m = 2.3, n = 4.9;
Add(a, m);//Add<int,double>(a,m);多個模板的手動推演
return 0;
}
3、模板和實例可以同時存在,編譯器會優先調用實例?
template<typename T>//可以寫typename也可以寫class
T Add(const T& a, const T& b)
{
return a + b;
}
int Add(const int& a, const int& b)
{
return a + b;
}
int main()
{
int a = 10, b = 5;
double m = 2.3, n = 4.9;
Add(a, m);//調用已有實例
Add<int>(a, m);//調用模板生成的實例
return 0;
}
1、模板和普通函數的函數名修飾規則是不一樣的。
2、模板和實例可以同時存在,編譯器會優先調用實例。如果想使用模板生成的實例,必須使用尖括號指定類型。
3、如果模板可以生成更加匹配的版本,編譯器將會生成這個匹配版本而不是使用那個已有但不太匹配的實例。
六、類模板
1、對象定義時需要顯式實例化
int main()
{
Stack<double> st1; // double
st1.Push(1.1);
Stack<int> st2; // int
st2.Push(1);
return 0;
}
函數模板可以通過傳參確定T的類型,但是類模板編譯器無法推演,必須要在對象定義時顯式實例化類型。
模板參數不同,他們就是不同的類型。st1和st2屬于不同的類定義出的兩個對象。所以不能有st1=st2,因為他們不是同一個類,除非針對這種賦值,自己寫一個賦值重載。
2、為什么stl被稱為模板
類模板和函數模板不同,需要在實例化的時候在類名后加上<類型>。
類模板不是真正的類,而實例化出來的才是真正的類。
// Vector是類模板,Vector<int>才是類型
Vector<int> s1;
Vector<double> s2;
原文鏈接:https://blog.csdn.net/gfdxx/article/details/127406881
相關推薦
- 2023-01-17 Golang時間及時間戳的獲取轉換超全面詳細講解_Golang
- 2022-08-19 python如何使用contextvars模塊源碼分析_python
- 2022-10-17 Kotlin編程循環控制示例詳解_Android
- 2022-12-01 SQL?Server數據庫分離和附加數據庫的操作步驟_MsSql
- 2024-03-10 【Redis】Redis的持久化(備份)
- 2022-12-24 C++返回值是類名和返回值是引用的區別及說明_C 語言
- 2023-07-26 webpack優化代碼運行之Code split
- 2022-08-08 Android實現頁面跳轉_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支