C++初探索

来源:https://www.cnblogs.com/baobaobashi/archive/2023/01/08/17029296.html
-Advertisement-
Play Games

上一篇博客中只是瞭解一下java文件是怎麼編譯的,但是一般來說大家都是使用編程軟體來進行開發,我是使用IntelliJ IDEA進行開發的官網下載IDEA(自行安裝哈):地址:https://www.jetbrains.com/idea/download/other.html使用IDEA我使用的id ...


C++初探索

前言

C++ 和 C 的區別主要在8個方面:

  1. 輸入和輸出
  2. 引用
  3. inline函數
  4. 函數預設值
  5. 函數重載
  6. 模板函數
  7. new 和 delete
  8. namespace

我僅對印象不深的地方做了總結。


目錄


一、引用初探索

1.引用的定義與區別

定義類型& 引用變數的名稱 = 變數名稱

'&' 不是取地址符嗎,怎麼又成為引用了呢?下麵將常見的 '&' 做一個區分

C中的 '&'

c = a && b; 		//此處 && 是 邏輯與

c = a & b;		//此處的 & 是 按位與

int *p = &a;		//此處的 & 是 取地址符
int &x = a;		//此處的 & 是 引用

void fun(int &a);	//此處的 & 也是引用

疑問:int &fun()這個是函數的引用嗎?
回答:不是函數的引用,表示函數的返回值是一個引用。

2.引用的要求

  • ①當定義引用時,必須初始化
    //正確用法:
    int a = 10;
    int &x = a;
    
  • ② 沒有引用的引用(沒有二級引用
    //錯誤用法:
    int &x = a;
    int &&y = x;
    
  • ③ 沒有空引用
    int &x;		//error:不存在空引用
    

3.引用與指針的區別

引用 指針
必須初始化 可以不初始化
不可為空 可以為空
不能更換目標 可以更換目標
沒有二級引用 存在二級指針
  • 1、引用必須初始化,而指針可以不初始化

    int &s;		//error:引用沒用初始化
    int *p;		//right:指針不強制初始化
    
  • 2、引用不可以為空,指針可以為空

    int &s = NULL;		//error:引用不可以為空,右值必須是已經定義過的變數名
    int *p = NULL;		//right:可以定義空指針。
    
    int fun_p(int *p)
    {
        if(p != NULL)		//因為指針可以為空,所以在輸出前需要判斷。
        {
            cout << *p << endl;
        }
        return *p;
    }
    
    int fun_s(int &s)
    {
        cout << s << endl;		//引用不為空,可以直接輸出
    
        return s;
    }
    
  • 3、引用不能改變目標,指針可以更換目標

    int main()
    {
    	int a = 20;
    	int b = 10;
    
    	int &s = a;
    	int *p = &a;
    
    	s = b;                  //引用只能指向初始化時的對象,如果改變,原先對象也跟著改變。
    	p = &b;                 //指針可以改變指向的對象,不改變原先對象。
    
    	cout << s << endl;      
    	cout << a << endl;
    
    	return 0;
    }
    

4.常引用

我們可以能力收縮,不可能力擴展

//error:能力擴展
int a = 10;
int& b = a;			//a b c都是一個東西
const int& c = a;		//常引用

b += 10;
a += 100;			//可以通過a b 去修改a 和 b
//c += 100;			//error:不可通過 c 來修改 a 或者 b
//能力收縮
const int a = 100;
int& b = a;			//不可編譯成功

//a += 100;			//error:a 自身不可改變
b += 100;

int a = 100;
const int& x = a;		//可以編譯成功

5.何時使用引用

引用可以作為函數參數

void Swap(int a, int b)	
{
	int tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	int x = 10, y = 20;
	Swap(x, y);		//僅僅Swap內部交換,並不能影響到實參
}
//加上引用,對a和b的改變會影響實參
void Swap(int &a, int &b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	int x = 10, y = 20;
	Swap(x, y);	//僅僅Swap內部交換,並不能影響到實參
}

二、inline內聯函數

1.內聯函數的定義

為瞭解決一些頻繁調用小函數消耗大量棧空間的問題,引入了inline內聯函數。

inline int fun(int a ,int b)
{
	return a + b;
}

2.內聯函數的處理流程

處理步驟

  • 將 inline 函數體複製到 inline 函數調用點處;
  • 為所用 inline 函數中的局部變數分配記憶體空間;
  • 將 inline 函數的的輸入參數和返回值映射到調用方法的局部變數空間中;
  • 如果 inline 函數有多個返回點,將其轉變為 inline 函數代碼塊末尾的分支(使用 GOTO)。
int fun(int a,int b)
{
	return a + b;
}

//普通調用
int main()
{
	int a = 10;
	int b = 20;
	int c = fun(10,20);
}

//內聯函數
int main()
{
	int a = 10;
	int b = 20;
	int c = 10 + 20;	//此處相當於直接展開函數
}

3.內聯函數與三者的區別

3.1 與普通函數的區別

內聯函數在函數的調用點直接展開代碼,沒有開棧和清棧的開銷。普通函數有開棧和清棧的開銷。
內聯函數要求代碼簡單,不能包含複雜的結構控制語句。
若內聯函數體過於複雜,編譯器將自動把內聯函數當成普通函數來執行。

3.2 與static函數的區別

static修飾的函數處理機制只是將函數的符號變成局部符號,函數的處理機制和普通函數相同,都有函數的開棧和清棧的開銷。內聯函數沒有函數的開棧和清棧的開銷。
inline函數是因為代碼直接在調用點展開導致函數只在本文件可見。而static修飾的函數是因為函數法符號從全局符號變成局部符號導致函數本文件可見。

3.3 與巨集定義的區別

inline函數的處理時機是在編譯階段處理的,有安全檢查和類型檢查。而巨集的處理是在預編譯階段處理的。沒有任何的檢查機制,只是簡單的文本替換。
inline函數是一種更安全的巨集。

4.僅realese版本才會產生內聯

代碼如下:

inline int Add_Int(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 10, b = 20;
	int c = 0;

	c = Add_Int(a, b);
	cout << c << endl;
	return 0;
}

debug版本的反彙編:仍是以函數調用的形式

image

release版本的反彙編:在編譯時期展開

image

5.inline函數使用的限制

  • 一般寫在頭文件中

  • 只在release版本中生效

  • inline只是一個建議,是否處理由編譯器決定

    如果函數體內的代碼比較長,使得內聯將導致記憶體消耗代價比較高。
    如果函數體內出現迴圈,那麼執行函數體內代碼的時間要比函數調用的開銷大。
    
  • 基於實現的,不是基於聲明的

    int fun(int x,int y);		// 函數聲明
    
    inline int fun(int x,int y)	// 函數定義
    {
    	return x+y;
    }
    
    // 定義時加inline關鍵字
    inline void fun1(int x)
    {
    
    }
    

三、函數的重載

在C語言中,函數名 是 函數的唯一標識
C++中,函數原型函數的標識

函數原型

函數原型 = 函數返回類型 + 函數名 + 形參列表(參數的類型和個數)

使用extern關鍵字指定為C語言編譯

extern"C" int Max(int a, int b)
{
	return a > b ? a : b;
}
extern"C" int fun(int a, int b)
{
	return a + b;
}

int main()
{
	Max(10, 20);
	fun(20, 30);
	return 0;
}

VS2022中

以C語言編譯:函數名仍是原來的函數名
image
以C++編譯:也是一樣的情況
image

將函數的形參類型進行修改:

int Max(int a, int b)
{
	return a > b ? a : b;
}

int Max(double a, int b)
{
	return a > b ? a : b;
}

int main()
{
	Max(10, 20);		//編譯正常
	Max(10.00, 20);		//編譯正常
	return 0;
}

發現函數名仍是一樣,但是存放double類型的寄存器與存放int類型的寄存器不一樣
image

再將返回值類型也進行修改:

int Max(int a, int b)
{
	return a > b ? a : b;
}

double Max(double a, int b)
{
	return a > b ? a : b;
}

int fun(int a, int b)
{
	return a + b;
}

int main()
{
	Max(10, 20);		//編譯正常
	Max(10.10, 20);		//編譯正常
	return 0;
}

發現並沒有任何問題,調用的也並非是同一個函數
image


在VS2019中:

以C語言編譯:函數名前加 _ ,_fun 和 _Max
image

以C++編譯:沒有下劃線,與VS2022中C++編譯相同


在VC6.0中:

int Max(int a, int b)
{
	return a > b ? a : b;
}

double Max(double a, double b)
{
	return a > b ? a : b;
}

char Max(char a, char b)
{
	return a > b ? a : b;
}

int main()
{
	Max(10, 20);
	Max(10.0, 10.0);
	Max('a','b');
	return 0;
}

返回值為int 類型的Max函數:
image

返回值為double類型的Max函數:
image

返回值為char類型的Max函數:
image
為什麼函數名發生了如此大的改變
這個就是名字粉碎技術

四、函數模板

模板的定義:

template <模板參數名>
返回類型 函數名(形式參數表)
{
	//函數體
}

<模板參數表> 尖括弧中不能為空,參數可以有多個,用,隔開

template<class Type>
void Swap(Type& a, Type& b)
{
	Type tmp = a;
	a = b;
	b = tmp;
}

註意1:<>中只能以C++的方式寫,不能出現如 template <struct Type>
註意2:下麵調用Swap不是簡單的替換(巨集替換),是重命名規則

//普通類型
template <class Type>				typedef int Type;
void Swap(Type &a ,Type &b)			void Swap<int>(Type &a,Type &b)
{						{
	Type tmp = a;					Type tmp = a;
	a = b;						a = b;
	b = tmp;					b = tmp;
}						}

int main()
{
	int a = 10, b = 20;
	Swap(a, b);		//此處的調用如同右邊函數
}
//指針類型
template<class Type>				typedef int Type
void fun(Type p)				void fun<int *>(Type p)
{						{
	Type a, b;					Type a, b;
}						}
int main()
{
	int x = 10;
	int* ip = &x;
	fun(ip);

	return 0;

}

註意3:編譯時進行重命名,非運行時。

五、new和malloc

//C語言與C++申請空間:
int main()
{
	int n = 10;
	
	//C:malloc free
	int* ip = (int*)malloc(sizeof(int) * n);
	//地址空間與NULl進行對比,判斷是否申請失敗
	if (NULL == ip)	exit(1);
	free(ip);
	ip = NULL;

	//C++:new delete
	ip = new int;
	*ip = 100;
	delete ip;
	ip = NULL;
}
//new申請連續空間
int main()
{
	//new申請連續空間,要釋放連續空間
	ip = new int[n];
	//此處的delete不是把ip刪除,而是將ip指向堆區的空間換給系統
	delete[]ip;	
}

new申請失敗時候的處理:

//錯誤處理:
	ip = new int;
	if (ip == NULL)	exit(1);
	delete ip;
	ip == NULL;
	
//new 如果分配記憶體失敗,預設是拋出異常的。
//分配成功時,那麼並不會執行 if (ip == NULL)
//若分配失敗,也不會執行 if (ip == NULL)
//分配失敗時,new 就會拋出異常跳過後面的代碼。

//正確處理1:強制不拋出異常
int *ip = new (std::nothrow) int[n];
if (ip == NULL)
{
	cout << "error" << endl;
}

//正確處理2:捕捉異常
try
{
	int* ip = new int[SIZE];
}
catch (const bad_alloc& e)
{
	return -1;
}

總結

仍遺留下很多問題:

  • new和malloc的區別
  • 命名空間
  • 引用的深入
  • const

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • -- 痞子衡維護的 NXP-MCUBootUtility 工具距離上一個大版本(v3.5.0)發佈過去 9 個月了,這一次痞子衡為大家帶來了版本升級 v4.0.0,這個版本主要有兩個重要更新需要跟大家特別說明一下。 一、v4.0更新記錄 二、幾個不可忽視的更新 2.1 更多MCU型號支持 v4.0 ...
  • qemu搭建和運行起來一個linux內核環境。 參考了博客: https://www.cnblogs.com/edver/p/6001786.html https://www.cnblogs.com/bigsissy/p/11134802.html https://www.cnblogs.com/z ...
  • 背景 https://www.cnblogs.com/liteng0305/p/17018299.html 上次使用樂鑫編譯好的OpenOCD失敗,可能是因為沒有開啟CMSIS-DAP支持,手動開啟編譯試一下 平臺 Ubuntu Linux 5.4.0 官方OpenOCD 直接下載的OpenOCD沒 ...
  • 分片是指跨機器拆分數據的過程,通過在每台機器上放置數據的子集,無須功能強大的機器,只使用大量功能稍弱的機器,就可以存儲更多的數據並處理更多的負載。 ...
  • Vue01 1.Vue是什麼? Vue(讀音/vju:/,類似於view)是一個前端框架,依據構建用戶界面 Vue的核心庫只關註視圖層,不僅易於上手,還便於與第三方庫或者項目整合 支持和其他類庫結合使用 開發複雜的單頁應用非常方便 Vue是Vue.js的簡稱 官網:Vue.js - 漸進式 Java ...
  • 我在實現在客服系統的時候,前端是基於WebSocket來實時收取服務端消息的,詳細的解釋下 即時通訊一種常用的方法是使用 WebSocket。WebSocket 是一種通信協議,它允許瀏覽器和伺服器進行全雙工通信,也就是說,雙方都可以同時發送和接收消息。 在前端使用 JavaScript 實現即時通 ...
  • 2023-01-04 最近想用Cesium給學校做一個類似智慧校園的東西,要做的東西很多,首先是獲取學校模型的問題,然後怎麼用Cesium載入3Dtile 1.獲取學校模型 想到之前被老師抓苦力去做春熙路的圖,於是決定用比較熟悉的OSM數據集 https://www.openstreetmap.or ...
  • 論文地址:https://pdos.csail.mit.edu/6.824/papers/raft-extended.pdf 看完raft共識演算法,腦袋非常懵,所以寫一篇學習筆記,記錄一下。 raft演算法主要解決三個模塊的問題:領導人選舉、日誌複製和安全性。當然除了這三個方面,論文對於raft的安全 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...