淺談——C++和C#差異

雖然這個話題看著似乎有些關公戰秦瓊的味道,但是作為游戲開發者,C++和C#一定是繞不開的兩門語言。不過雖然說是比較二者差異,因為我學習的過程主要是先學C++,所以我先基于C++的認知,再來聊聊C#之中的不同。

(為什么會想到寫這個帖子是因為我發現老是記混二者名字相同但是底層完全不同的概念,只能說很煩)

討論差異之前,我們先來聊聊大體上二者的相似之處。

二者都是編譯執行的強類型語言,都是基于OOP思想,都支持泛型。

我們首先來聊第一個不同的點——C++的指針。

指針

C++中的指針是一個用來存儲變量地址的變量,其底層涉及到了內存管理的內容,眾所周知指針給C++帶來了高性能的同時也帶了很多麻煩,指針本身的管理就是一個麻煩,比如什么野指針,懸空指針,空指針,指針與指針之間又可以傳遞,還有一系列諸如常量指針和指針常量的內容。

C#中并非不可以使用指針,C#中有內存分層的概念:

針對原生內存我們就需要像C++一樣通過指針去訪問和管理,但是不討論這種特殊情況的話,在C#的代碼中我們是看不到指針的,那這時候我們還是需要一個存儲變量地址的變量呀,不然怎么進行內存的訪問呢?這個就是我們C#的引用類型。

這里我先說一下C#的數據類型分類,大體上分成值類型和引用類型。

這里補充一下關于System.Object和System.ValueType的內容:

Object類是C#中所有對象的基類。

通過上述的介紹,相比你也知道了,引用類型就是一個類型安全的托管指針,用來存儲堆中對象的地址,我們修改引用類型的值就會直接修改實際的數據,我們拷貝值類型時會拷貝出一個副本而拷貝引用類型會拷貝實際的數據。

這里又涉及到一個問題,我們知道修改C++的指針時,我們既可以修改指針指向的地址也可以通過指針修改地址存儲的值,那我們修改對應的C#的引用類型的變量時,如何具體判斷修改的是地址還是地址存儲的值呢?

先說結論的話,C#的引用是無法修改存儲的地址的值的,所以我們不用區分,因為一般情況下我們只能修改引用類型的值。

難道我們就沒有辦法去修改引用指向的地址嗎?

ref關鍵字可以幫助我們實現引用的地址修改。

?

C# 引用類型實現了 C++ 指針的核心權能——提供對堆內存對象的間接訪問與修改能力,并支持高效的數據共享,移除了指針算術、任意重定向等危險操作,通過 GC 自動管理生命周期,體現了 C# ??“安全優先”的設計理念。?

抽象類&&接口

C++如何實現抽象類?很簡單,用一個純虛函數來做就好了。

class A {
public:               // ? 必須 public 才能被覆蓋virtual void a() = 0; // ? 虛函數需 virtual
};

但是當你來到C#中,你發現好像沒有純虛函數這種寫法,取而代之的是abstract關鍵字:

public abstract class A { // ? 類必須標記 abstractpublic abstract void a(); // ? 無方法體,無{},直接分號結束
}

這個概念同樣地影響到了接口interface的實現:

// C++ 通過純抽象類模擬接口(無成員變量,無實現方法)
class IShape {
public:virtual ~IShape() = default;        // 虛析構(必須)virtual double GetArea() const = 0; // 純虛方法(接口函數)virtual void Draw() const = 0;       // 純虛方法(接口函數)
};

這是C++的接口實現,所有接口內的方法都必須是純虛函數。

// C# 原生接口(無成員變量,無構造邏輯)
public interface IShape {double GetArea();    // 接口方法(自動public)void Draw();         // 接口方法(自動public)
}

C#中已經專門集成了接口的概念,用關鍵字interface即可。

總結來說的話,C的抽象類是通過我們的一個純虛函數來實現的,某一個類之中如果有一個純虛函數我們就把這個類認作這個抽象類。然后C++的接口就是類中的所有函數都是純虛函數我們就把這個類看做一個接口,繼承這個接口的方法就必須要提供這些方法的全部實現。然后C#的話關于抽象類它有一個專門的關鍵字叫abstract,我們用這個關鍵字修飾類那么這個類就是一個抽象類,要求其中的這個類中的至少有一個方法也是被abstract修飾的抽象方法,而我們的那個接口的話也是有原生集成的一個interface關鍵字,我們用interface關鍵字來修飾類那么這個類就會被視作一個接口我們只需要在這個類中來呃提供方法的聲明就可以。

只讀

在C++中,實現只讀的關鍵字有constexpr和const。

對于C++來說,這兩個關鍵字最大的差異就是具體何時賦予變量只讀的屬性,對應的,什么時候賦予只讀的屬性就要求什么時候進行初始化。而在C#中也有兩個關鍵字實現只讀,但是這兩個關鍵字變成了const和read only。

可以看到同樣叫const,在C++中是運行時常量結果到了C#變成了編譯常量。

String

C#的string作為一個引用類型,我們在討論值類型和引用類型的區別時有提到,修改值類型往往都是修改其副本而修改引用類型往往都是直接修改其值,那么string作為引用類型,一定也是這樣的吧?

如果真是這樣我就不會在這里把這個點列舉出來了,實時上C#中的string非常特殊:具有不可變性

如何理解不可變性?當我們寫這樣一段代碼:

string a="abc";
a+='c';

首先a本身就是一個string的對象,一開始的時候是一個沒有內容(但是有內存)的空字符串"",然后"abc"本身也是一個字符串,我們把"abc"丟給a,然后我們后續要對a添加字符'c'時,c本身也是一個字符(串?雙引號就是字符串單引號就是字符),由于C#中string的不可變性,我們的堆中會重新生成一個a的副本,然后把'c'丟給a的副本,然后返回a的副本,a本身就作為堆上的垃圾等待回收了。

一言以蔽之,C#將string賦予了不可變性,這是有理由的:

可以看到賦予string不可變性后,線程安全的問題就從根源上被解決了。

迭代器

C++中的迭代器是STL庫中幫助我們訪問容器內部具體元素的一個封裝了指針的工具,但是C#中可沒有STL庫,更沒有指針用來封裝,那么C#的迭代器是干嘛的呢?

聊到這個,我需要先來說一下C#的foreach和for這兩種數據遍歷方式。

foreach是只讀循環,不可以修改遍歷的元素,不用獲取數組的長度,以及由額外的GC開銷。前兩者非常好理解,但是第三點額外的GC開銷是從何而來的呢?

這里其實也可以看到C#底層的數據集合實現,很多都是基于IEnumerable接口實現的,這樣我們調用foreach進行遍歷時直接調用接口的方法獲取一個迭代器來遍歷元素。然后這里還牽扯到一個狀態機類:

當我們使用yield return方法時,編譯器就會幫助我們實現一個狀態機類實時記錄方法執行到哪一步,這個狀態機對象本身在堆上,所有會有GC開銷。?

Using?

這里算是一個補充,C++和C#中的using都有著引用命名空間以及修改命名空間命名的作用,但是C#中還多了一個功能就是可以幫助我們釋放實現了IDisposable接口的資源。

委托/回調

C++中并沒有委托這個概念,但是有基本的回調函數的概念,具體來說就是允許在函數的參數列表中放入函數的指針,然后在函數體中直接利用這個指針來調用函數體以外的函數。

  void callback(int x) { /* ... */ }void doSomething(void (*func)(int)) {func(42); // 回調}

C#中的委托其實本質上也是一樣的思想:不過delegate是一種類型安全的函數指針。

  public delegate void MyCallback(int x);void DoSomething(MyCallback cb) {cb(42); // 回調}

在這個delegate的基礎之上,C#還實現了event,action和function。

C# 中的?delegate?是類型安全的函數指針,是回調和事件機制的基礎;event?是對委托的事件化封裝,實現發布-訂閱模式,限制了外部訪問;Action?和?Func?是?.NET 內置的泛型委托,分別用于無返回值和有返回值的場景,極大簡化了委托的聲明和使用。四者本質上都是委托機制的不同表現形式,適用于不同的開發需求。

結構體

有一個最基本的差異:C++的結構體支持繼承而C#的不支持。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/92285.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/92285.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/92285.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

rocky9-zabbix簡單部署

目錄 一、準備 1、(rocky9) 2、配置數據庫 二、配置文件 1、導入初始架構與數據 2、配置相關文件 三、啟動服務 1、瀏覽器訪問 2、解決亂碼問題 ?編輯 四、監控 ① 添加主機 1、修改配置文件 2、啟動服務 3、網頁添加 ②添加監控模塊 1…

tabBar設置底部菜單選項、iconfont圖標(圖片)庫、模擬京東app的底部導航欄

歡迎來到我的UniApp技術專欄!🎉 在這里,我將與大家分享關于UniApp開發的實用技巧、最佳實踐和項目經驗。 專欄特色: 📱 跨平臺開發一站式解決方案 🚀 從入門到精通的完整學習路徑 💡 實戰項目經…

7.22總結mstp,vrrp

一、MSTP技術MSTI和MSTI域根MSTP中的端口角色3. MSTP工作原理 MSTP 計算方法? CST/IST的計算和RSTP類似 ? MSTI的計算僅限于區域內 ? MSTI計算參數包含在IST BPDU中,和IST的計 算同步完成&#xfe…

【電腦】網卡的基礎知識

網卡(Network Interface Card, NIC)是計算機中用于連接網絡的關鍵組件之一,它負責管理和發送數據包到互聯網或其他局域網設備。下面是一些關于網卡的詳細知識:網卡的基本結構MAC地址:每個網卡都有一個唯一的物理地址&a…

IPv4枯竭時代:從NAT技術到IPv6的演進之路

🔍 開發者資源導航 🔍🏷? 博客主頁: 個人主頁📚 專欄訂閱: JavaEE全棧專欄 IPv4(Internet Protocol version 4)是互聯網最核心的通信協議之一,自 1981 年正式標準化以來…

模式結構-微服務架構設計模式

需求(Forces)結果上下文(Resulting context)相關模式(Related patterns)需求:必須解決的問題需求部分描述了必須解決的問題和圍繞這個問題的特定上下文環境。需求有時候是相互沖突的,所以不能指望把他們全部都解決(必須取舍&#…

30個常用的Linux命令匯總和實戰場景示例

下面匯總常用的 30 個常用的 Linux 命令,每個都附有簡要說明和典型示例,適合日常開發、服務器維護或系統學習使用。30 個常用的 Linux 命令匯總 一、文件與目錄操作(基礎)命令說明示例ls列出文件和目錄ls -l 顯示詳細信息cd切換目…

Taro 網絡 API 詳解與實用案例

Taro 網絡 API 詳解與實用案例 在現代前端開發中,網絡通信是不可或缺的一環。Taro 作為一款多端開發框架,提供了豐富且統一的網絡 API,幫助開發者在小程序、H5、React Native 等多端環境下高效地進行數據交互。本文將詳細介紹 Taro 的四大網…

Bitbucket平臺的HTTP Access Tokens操作手冊

在Bitbucket平臺添加HTTP Access Tokens(用于替代密碼進行認證)。 1. 登錄Bitbucket并訪問個人設置 打開 Bitbucket 并登錄賬號。點擊右上角頭像 → 選擇 Manage account。 2. 生成Access Token 在左側菜單中選擇 Access tokens(位于 Sec…

低成本、高泛化能力的無人機自主飛行!VLM-Nav:基于單目視覺與視覺語言模型的無地圖無人機導航

作者:Gobinda Chandra Sarker1^{1}1, AKM Azad2^{2}2, Sejuti Rahman1^{1}1, Md Mehedi Hasan1^{1}1單位:1^{1}1達卡大學,2^{2}2伊瑪目穆罕默德伊本沙特伊斯蘭大學論文標題:VLM-Nav: Mapless UAV-Navigation Using Monocular Visi…

Docker Desktop 安裝到D盤(包括wsl)

默認WSL虛擬機位置&#xff1a; C:\Users\<用戶名>\AppData\Local\Docker\wsl重裝DockerDesktop下載安裝包Docker Desktop Installer.exe在D盤創建文件夾D:\Program Files\DockerDesktopD:\Program Files\DockerDesktop\data 在cmd運行 start /w "" "Dock…

網絡協議(三)網絡層 IPv4、CIDR(使用子網掩碼進行網絡劃分)、NAT在私網劃分中的應用

利用子網掩碼進行子網劃分 這是一個模擬搭建的私網&#xff0c;有倆臺主機ab。現在主機a要給云端服務器發送一條消息&#xff0c;這條消息怎么才能到達云端服務器呢&#xff1f;確定這條數據中的源端為本地ip的9000端口&#xff0c;目的端為24.24.24.8888端口&#xff0c;首先&…

8.4 Java 原生 TCP Socket 實現 HTTP 請求解析和請求分發

使用 Java 原生 TCP Socket 實現 HTTP 請求解析和請求分發&#xff0c;是一個理解 HTTP 協議底層原理的好方法。雖然 Java 提供了 HttpServer 類來簡化 HTTP 服務器開發&#xff0c;但如果你想從 TCP 層 開始構建一個簡單的 HTTP 服務器&#xff0c;可以使用 ServerSocket 和 S…

自研能管項目開發界面

自研能管軟件實現一個界面開發 目的&#xff1a; ? 通過接口方式實現展示哪些數據例如&#xff1a; ? 已知制絲車間下的計量電表&#xff0c;在可視化界面通過點擊制絲車間的方式&#xff0c;自動在MySQL存儲制絲車間的電表數據(假設是每分鐘存儲一次)&#xff0c;前端即可以…

【NLP輿情分析】基于python微博輿情分析可視化系統(flask+pandas+echarts) 視頻教程 - 基于wordcloud庫實現詞云圖

大家好&#xff0c;我是java1234_小鋒老師&#xff0c;最近寫了一套【NLP輿情分析】基于python微博輿情分析可視化系統(flaskpandasecharts)視頻教程&#xff0c;持續更新中&#xff0c;計劃月底更新完&#xff0c;感謝支持。今天講解基于wordcloud庫實現詞云圖 視頻在線地址&…

Vue3 面試題及詳細答案120道(31-45 )

《前后端面試題》專欄集合了前后端各個知識模塊的面試題&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

Proxmox VE 8.3/8.4開啟Intel 核顯的vGPU,搭建vGPU云桌面系統

1. Intel Graphics SR-IOV 介紹Intel Graphics SR-IOV 是 Intel 最新的圖形虛擬化技術。單根 I/O 虛擬化 &#xff08;SR-IOV&#xff09; 通過將設備劃分為多個虛擬功能來定義共享物理設備功能的標準方法。每個虛擬函數都直接分配給一個虛擬機&#xff0c;從而為虛擬機實現接近…

LeetCode 熱題100

文章目錄哈希兩數之和字母異位詞分組最長連續序列雙指針移動零盛最多水的容器滑動窗口子串多刷題 LeetCode 熱題100 哈希 兩數之和 思路分析&#xff1a; 暴力做法&#xff1a;每一個數字都與剩余的數字作比較&#xff0c;時間復雜度是O(n2)O(n^2)O(n2)哈希做法&#xff1a;我…

Idea或Pycharm上.idea的忽略提交的問題總結

文章目錄問題描述如果是首次提交或者之后的提交代碼時把.idea及其文件提交到遠端倉庫中&#xff0c;此時再創建.gitignore會不生效。問題描述 由于在代碼托管平臺上創建的項目&#xff0c;沒有關聯創建.gitignore文件。導致git 克隆到本地電腦上時&#xff0c;項目的根目錄下也…

【鎖】MySQL中有哪幾種鎖?

&#x1f4da; 歡迎來到我的Java八股文專欄&#xff01; &#x1f389; 各位程序員小伙伴們好呀~ &#x1f44b; 我是雪碧聊技術&#xff0c;很高興能在CSDN與大家相遇&#xff01;? &#x1f680; 專欄介紹 這個專欄將專注于分享Java面試中的經典"八股文"知識點 &a…