您好,登錄后才能下訂單哦!
前言
這是一篇C#開發重新學習C++的體驗文章。
作為一個C#開發為什么要重新學習C++呢?因為在C#在很多業務場景需要調用一些C++編寫的COM組件,如果不了解C++,那么,很容易。。。注定是要被C++同事忽悠的。
我在和很多C++開發者溝通的時候,發現他們都有一個非常奇怪的特點,都很愛裝X,都覺得自己技術很好,還很愛瞧不起人;但如果多交流,會發現更奇怪的問題,他們幾乎都不懂代碼設計,面向對象和業務邏輯的代碼寫的也都很爛。
所以,這次重溫C++也是想了解下這種奇異現象的原因。
C++重溫
首先打開VisualStudio,創建一個C++的Windows控制臺應用程序,如下圖:
圖中有四個文件,系統默認為我打開了頭文件和源文件的文件夾。
系統這么做是有意義的,因為剛學習時,外部依賴項,可以暫時不用看,而資源文件夾是空的,所以我們只專注這兩個文件夾就可以了。
作為一個C#開發,我對C++就是一知半解,上學學過的知識也都忘記的差不多了,不過,我知道程序入口是main函數,所以我在項目里先找擁有main函數的文件。
結果發現ConsoleTest.cpp文件里有main函數,那么,我就在這個文件里開始學習C++了,而且它的命名和我項目名也一樣,所以很確定,它就是系統為我創建的項目入口文件。
然后我打開ConsoleTest.cpp文件,定義一個字符串helloworld,準備在控制臺輸出一下,結果發現編譯器報錯。。。只好調查一下了。
調查后得知,原來,c++里沒有string類型,想使用string類型,只能先引用string的頭文件,在引用命名空間std,如下:
頭文件
頭文件到底是什么呢?
頭文件,簡單來說就是一部分寫在main函數上面的代碼。
比如上面的代碼,我們將其中的引用頭文件和使用命名空間的代碼提取出來,寫進pch.h頭文件;然后,我們得到代碼如下圖:
pch.h頭文件:
ConsoleTest.cpp文件:
也就是說,頭文件是用來提取.cpp文件的代碼的。
呃。。。好像頭文件很雞肋啊,一個文件的代碼為什么要提取一部分公共的?寫一起不就好了!為什么要搞個文件來單獨做,多傻的行為啊!
好吧,一開始我也的確是這么想的。
后來我發現,頭文件,原來并不是單純的提取代碼,還是跨文件調用的基礎。
也就是說,ConsoleTest.cpp文件,想調用其他Cpp文件的變量,必須通過頭文件來調用。
比如,我新建一個test.cpp和一個test.h文件。
然后我在test.cpp中,定義變量test=100;如下:
#include "pch.h"#include "test.h"int test = 100;
接著我在test.h文件中再聲明下test變量,并標記該變量為外部變量,如下。
現在,我在回到ConsoleTest.cpp文件,引用test.h文件;然后我就可以在ConsoleTest.cpp文件中使用test.cpp中定義的test變量了,如下:
如上述代碼所示,我們成功的輸出了test變量,其值為100。
到此,我們應該了解到了,頭文件的主要作用應該是把被拆散的代碼,扭到一起的紐帶。
----------------------------------------------------------------------------------------------------
PS:我在上面引用字符串頭文件時,使用的引用方法是【#include <string>】;我發現,引用該頭文件時,并沒有加后綴.h;我把后綴.h加上后【#include <string.h>】,發現編譯依然可以通過。
簡單的調查后得知,【#include <string>】是C++的語法,【#include <string.h>】是語法。因為C++要包含所有C的語法,所以,該寫法也支持。
Cin與Cout
Cin與Cout是控制臺的輸入和輸出函數,我在測試時發現,使用Cin與Cout需要引用iostream頭文件【#include <iostream>】,同時也要使用命名空間std。
在上面,我們提到過,使用字符串類型string時,需要引用頭文件string.h和使用命名空間std,那么現在使用Cout也要使用命名空間std。這是為什么呢?
只能推斷,兩個頭文件string.h和iostream.h在定義時,都定義在命名空間std下了。而且,通過我后期使用,發現還有好多類和類型也定義在std下了。
對此,我只能說,好麻煩。。。首先,缺失基礎類型這種事,就很奇怪,其次不是一個頭文件的東西,定義到一個命名空間下,也容易讓人混亂。
不過,對于C++,這么做好像已經是最優解了。
----------------------------------------------------------------------------------------------------
PS:Cin與Cout是控制臺的輸入和輸出函數,開始時,我也不太明白,為什么使用這樣兩個不是單詞的東西來作為輸入輸出,后來,在調查資料時,才明白,原來這個倆名字要拆開來讀。
讀法應該是這樣的C&in和C&out,這樣我們就清晰明白的理解了該函數了。
define,typedef,指針,引用類型,const
define
首先說define,define在C++里好像叫做宏。就定義一個全局的字符串,然后再任何地方都可以替換,如下:
也就是說,define定義的宏,在C++里就是個【行走的字符串】,在編譯時,該字符串會被替換回最初定義的值。這。。。這簡直就是編譯器允許的bug。。。
不過,它當然也有好處,就是字符串更容易記憶和理解。但是說實話,定義一個枚舉一樣好記憶,而且適用場景更加豐富,所以,個人感覺這個功能是有點雞肋,不過C++好多代碼都使用了宏,所以還是需要了解起來。
typedef
typedef是一個別名定義器,用來給復雜的聲明,定義成簡潔的聲明。
如上述代碼所示,我定義了一個結構體kiba_Org,如果我要用kiba_Org聲明一個變量,我需要這樣寫【struct kiba_Org korg】,必須多寫一個struct。
但我如果用typedef給【struct kiba_Org korg】定義一個別名kiba,那么我就可以直接拿kiba聲明變量了。
呃。。。對此,我只能說,為什么會這么麻煩!!!
以為這就很麻煩了嗎?NO!!!還有更麻煩的。
比如,我想在我定義的結構體里使用自身的類型,要怎么定義呢?
因為在C++里,變量定義必須按照先聲明后使用的【絕對順序】,那么,在定義時就使用自身類型,編譯器會提示錯誤。
如果想要讓編譯器通過,就必須在使用前,先給自身類型定義個別名,這樣就可以在定義時使用自身類型了。
呃。。。好像有點繞,我們直接看代碼。
如上述代碼所示,我們在定義結構體之前,先給它定義了個別名。
那么,變量定義不是必須按照先聲明后使用的【絕對順序】嗎?為什么這里,又在定義前,可以定義別名了呢?這不是矛盾了嗎?
不知道,反正,C++就是這樣。。。就這么屌。。。
指針
指針在C++中,就是在變量前加個*號,下面我們定義個指針來看看。
如上述代碼所示,我們定義了倆指針,int *ipointer和int* ipointer2。可以看到,我這倆指針的*一個靠近變量一個靠近聲明符int,但兩種寫法都正確,編譯器可以編譯通過。
呃。。。就是這么屌,學起來就是這么優雅。。。
接著,我們用取地址符號&,取出i變量的地址給指針,然后指針變量*ipointer中ipointer存儲的是i的地址,而*ipointer存儲的是518,如下圖:
那么,我們明明是把i的地址給了變量*ipointer,為什么*ipointer存儲的是518呢?
因為。。。就是這么屌。。。
哈哈,不開玩笑了,我們先看這樣一段代碼,就可以理解了。
如上述代碼所示,我把聲明和賦值給分開了,這樣就形象和清晰了。
我們把i的地址給了指針(*ipointer)中的ipointer,所以ipointer存的就是i的地址,而*ipointer則是根據ipointer所存儲的地址找到對應的值。
那么,int *ipointer = &i;這樣賦值是什么鬼?這應該報錯啊,應該不允許把i的地址給*ipointer啊。
呃。。。還是那句話,就是這么屌。。。
->
->這個符號大概是指針專用的。下面我們來看這樣一段代碼來了解->。
首先我們定義一個kiba結構體的實例,定義定義一個kiba結構體的指針,并把kinstance的地址給該指針。
此時,如果我想為結構體kiba中的字段id賦值,就需要這樣寫【(*kpointer).id = 518】。
我必須把*kpointer擴起來,才能點出它對應的字段id,如果不擴起來編譯器會報錯。
這樣很麻煩,沒錯,按說,微軟應該在編譯器中解決這個問題,讓他*kpointer不用被擴起來就可以使用。
但很顯然,微軟沒這樣解決,編譯器給的答案是,我們省略寫*號,然后直接用存儲地址的kpointer來調用字段,但調用字段時,就不能再用點(.)了,而是改用->。
呃。。。解決的就是這么優雅。。。沒毛病。。。
引用類型
我們先定義接受引用類型的函數,如下。
如上述代碼所示,u經過函數usage后,他的值被改變了。
如果我們刪除usage函數中變量i前面的&,那么u的值就不會改變。
好了,那么&符號不是我們剛才講的取地址嗎?怎么到這里又變成了引用符了呢?
還是那句話。。。就是這么屌。。。
呃。。。還有更屌的。。。我們來引用個指針。
如上述代碼所示,我定義了兩個結構體變量kiunew,kiu,和一個指針*kiupointer,然后我把kiu的地址賦值給指針。
接著我把指針和kiunew一起發送給函數usagePointer,在函數里,我把指針的地址改成了kiunew的地址。
運行結果如下圖。
可以看到,指針地址已經改變了。
如果我刪除掉函數usagePointer中的【引用符&】(某些情況下也叫取地址符)。我們將得到如下結果。
我們從圖中發現,不僅地址沒改變,賦值也失敗了。
也就是說,如果我們不使用【引用符&】來傳遞指針,那么指針就是只讀的,無法修改。
另外,大家應該也注意到了,指針的引用傳遞時,【引用符&】是在*和變量之間的,如果*&k。而普通變量的引用類型傳遞時,【引用符&】是在變量前的,如&i。
呃。。。指針,就是這么屌。。。
const
const是定義常量的,這里就不多說了。下面說一下,在函數中使用const符號。。。沒錯,你沒看錯,就是在函數中使用const符號。
如代碼所示,我們在入參inti前面加上了const修飾,然后,我們得到這樣的效果。
i在函數constusage,無法被修改,一但賦值就報錯。
呃。。。基于C#,估計肯定不好理解這個const存在的意義了,因為如果不想改,就別改啊,標只讀這么費勁干什么。。。
不過我們換位思考一下,C++中這么多內存控制,確實很亂,有些時候加上const修飾,標記只讀,還是很有必要的。
PCH
在項目創建的時候,系統為我們創建了一個pch.h頭文件,并且,每個.cpp文件都引用了這個頭文件【#include "pch.h"】。
打開.pch發現,里面是空代碼,在等待我們填寫。
既然.pch沒有被使用,那么將【#include "pch.h"】刪掉來簡化代碼,刪除后,發現編譯器報錯了。
調查后發現,原來項目在創建的時候,為我們設置了一個屬性,如下圖。
如圖,系統我們創建的pch.h頭文件,被設置成了預編輯頭文件。
下面,我修改【預編譯頭】屬性,修改為不使用預編譯頭,然后我們再刪除【#include "pch.h"】引用,編譯器就不會報錯了。
那么,為什么創建文件時,會給我們設置一個預編譯頭呢?微軟這么做肯定是有目的。
我們通過名字,字面推測一下。
pch.h是預編譯頭,那么它的對應英文,大概就是PrecompileHeader。即然叫做預編譯,那應該在正式編譯前,執行的編譯。
也就是,編譯時,文件被分批編譯了,pch.h預編譯頭會被提前編譯,我們可以推斷,預編譯頭是用于提高編譯速度的。
類
C++是一個同時面向過程和面向對象的編程語言,所以,C++里也有類和對象的存在。
類的基礎定義就不多說了,都一樣。
不過在C++中,因為,引用困難的原因(上面已經描述了,只能引用其他.cpp文件對應的頭文件,并且,.cpp實現的變量,還得在頭文件里外部聲明一下),所以類的定義寫法也發生了改變。
C++中創建類,需要在頭文件中聲明函數,然后在.cpp文件中,做函數實現。
但是這樣做,明顯是跨文件聲明類了,但C++中又沒有類似partial關鍵字讓倆個文件合并編譯,那么怎么辦呢?
微軟給出的解決方案是,在.Cpp文件中提供一個類外部編寫函數的方法。
下面,我們簡單的創建一個類,在頭文件中聲明一些函數和一些外部變量,然后在.cpp文件中實現這些函數和變量。
右鍵頭文件文件夾—>添加——>類,在類名處輸入classtest,如下圖。
然后我們會發現,系統為我們創建了倆文件,一個.h頭文件和一個.cpp文件,如下圖。
然后編寫代碼如下:
classtest.h頭文件:
calsstest.cpp文件:
調用測試代碼如下:
結語
通過重溫,我得出如下結論。
一,C++并不是一門優雅的開發語言,他自身存在非常多的設定矛盾和混淆內容,因此,C++的學習和應用的難度遠大于C# ;其難學的原因是C++本身缺陷導致,而不是C++多么難學。
二,指針是C++開發學習設計模式的攔路虎,用C++學習那傳說中的26種設計模式,還勉強可以;但,如果想學習MVVM,AOP等等這些的設計模式的話,C++的指針會讓C++開發付出更多的代碼量,因此多數C++開發對設計模式理解水平很低也是可以理解的了。
三,通過學習和反思,發現,我曾經接觸的那些愛裝X的C++開發,確實是坐井觀天、夜郎自大,他們的編寫代碼的思維邏輯,確確實實是被C++的缺陷給限制住了。
----------------------------------------------------------------------------------------------------
到此,我重溫C++的心路歷程就結束了。
代碼已經傳到Github上了,歡迎大家下載。
Github地址:https://github.com/kiba518/C-ConsoleTest
----------------------------------------------------------------------------------------------------
總結
以上所述是小編給大家介紹的一個C#開發者重溫C++的心路歷程,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。