1.多值返回
在C/C++里面如果需要返回多值,一般是在函數傳入指針或者引用,比如
fun(int *a,int *b,int *c),但在go里面,如果需要返回多值,只需要把函數寫成這樣
1 func test_func()(int,int,int){ 2 a := 1; 3 b := 2; 4 c := 3; 5 6 return a,b,c; 7 }
最后函數會依次返回a,b,c
這個其實在lua中早就有了,所以實際上在go里面也算不上什么新的東西,go里面還有一個返回值命名的特性
func read_file()(read_count int,err int){
....//代碼需要做的事情
return ;
}
這樣就可以讓函數的代碼更加清晰和更讓人理解,看函數聲明就可以知道返回值是干什么的
2.零值初始化,自動推導類型
C++ 11新增了一個auto,在go里面也有:=符號可以自動推導,比如a := 1,a會自動被推導成為int類型,還有另外一個特性就是靈芝初始化,我想這個是大部分C程序員喜聞樂見的,因為這樣我們可以少寫幾行代碼跑去初始化一個變量(在我的大部分代碼中也都是用0來初始化變量),如果C/C++有這個特性,實際上很多錯誤都可以很容易找到,我看很多代碼都是因為沒有初始化造成的
3.垃圾回收
這個不用說了,絕對是個利器,資源管理一直是C/C++頭疼的問題,當然自動垃圾回收肯定會帶來性能下降,甚至會內存泄露(據說java也會有內存泄露的問題,python是有的,雖然這些都有垃圾回收的機制)
4.slices
說白了slices就是指向一個array底層的指針,不過和array不同,slices在元素加入的時候可以增加長度,並且slices有點類似與指針,一旦修改了slices,那么slices指向的數據也會改變,go里面沒有把數組退化成指針的做法,如果你傳入的是個數組,那么go會復制整個數組的副本,也就是如果有100個元素,那么go就會復制100個元素的副本
5:defer
defer就是在函數退出的時候會自動調用我們用defer生命的代碼段,這個是為了防止我們忘記關閉文件句柄或者釋放資源,C/C++程序員經常犯的錯誤
1 f,_ = os.Open(file_name); 2 3 defer f.Close()
函數在打開文件后並不會立即執行f.Close,而是會在函數執行完畢后才執行f.Close()
6:接口,自定義類型與方法
1 class Graph 2 { 3 public: 4 int GetWidth(); 5 6 int GetHeight(); 7 8 virtual void Draw(); 9 private: 10 int width; 11 int height; 12 }
我一直不喜歡C++的這種的方式,因為把一大堆的函數和數據放在一起,這樣當代碼多了以后將會變得很混亂,而且因為虛函數的存在,在進行初始化的時候不能直接用memset或者memcpy,如果一個類中有幾百個變量,那么我們需要一個個去手動初始化,不像C語言里面,數據結構都是原生的值,可以直接memset初始化,go里面則是自動幫我們零值初始化
實際上有了解C++的應該知道,上面的這個類編譯器在生成代碼的時候還是幫我們進行了分開,比如GetAge()會變成GetAge(Person &person),在go里面則是將一個類分成三個部分,數據,方法與接口
1 type Graph struct{ 2 width int; 3 height int; 4 } 5 6 7 func (g *Graph)GetWidth()(int){ 8 return g.width; 9 } 10 11 func(g *Graph)GetHeight()(int){ 12 return g.height; 13 } 14 15 func(g *Graph)Draw(){ 16 fmt.Printf("graph draw"); 17 } 18 19 type graph_interface interface{ 20 Draw() 21 } 22 23 func Draw(g graph_interface){ 24 g.Draw(); 25 }
interface就是聲明了一個接口,就是類似與虛函數的vptr,可以把type graph_interface interface這句理解成某個把函數加入虛函數表,使用這個接口就可以調用傳入的參數的Draw這個函數(C++虛函數的實現原理也是利用這個方法)
在函數聲明前面加上(g *Graph)就可以把類的數據與這些方法綁定在一起,其他也沒什么好說的了,公有和私有數據或者函數都是利用大小寫來區分的,不過go里面跟C++不同,C++如果是private的話其他類就不能訪問這個變量或者函數,而go則是其他文件不能訪問,本文件還是可以訪問,有點類似於C的static
實際上go的C++內部的這些實現原理我估計都是差不多的,只是展現出來的語法的不同而已,當然到現在為止我更喜歡go的語法,我想go的設計思想更符合linus的說法
“爛程序員關心的是代碼。好程序員關心的是數據結構和它們之間的關系。”
git的設計其實非常的簡單,它的數據結構很穩定,並且有豐富的文檔描述。事實上,我非常的贊同應該圍繞我們的數據結構來設計代碼,而不是依據其它的,我認為這也是git之所以成功的原因之一[...]依我的觀點,好程序員和爛程序員之間的差別就在於他們認為是代碼更重要還是數據結構更重要。
我想C++的程序員要看看這篇文章:http://www.aqee.net/torvalds-quote-about-good-programmer/
將數據結構和這些方法分開將會更有助於程序員去理清數據結構與函數的關系
7.goroutine
據說這個是go的最大的"賣點",網上吹捧的文章也很多,不過lua很早就有這個東西了,所以我覺得沒什么可以吹捧的,就是在線程內再模擬單核的電腦去運行代碼段,當然這樣很節省資源,不用進行線程的切換
go func_name(),這樣就把一個函數當作goroutine來運行,當然routine並不是並發的,只是並行的,所以說goroutine是在線程內模擬單核的電腦去運行代碼(並發和並行的區別)
go routine進行通訊也很簡單,就是利用channel,有用過unix shell的應該很熟悉這個東西了,不多說了
go的缺點:
1.無法與C結合,無法編譯成lib,dll
總之看了這么多,我覺得go還是很好用的,但是到目前為止,沒發現有什么可以將go嵌入到c語言里面,只能把C嵌入到go里面,這個我想會導致很多公司不願意用go,我本來想將go弄進一個項目里面,但是發現go無法嵌入到C里面,也無法編譯成dll就放棄了
2.並不是像網上說的那樣是為了並發而設計的,只是改進的C++
應該能取代C++或者java,但估計不能取代C,網上有人拿go和erlang比較,我還是覺得go無法跟erlang比較,go頂多算是改進的C++,和erlang這種天生為了並行運算的而生的無法比較,go還是沿用了C/C++的面向對象,面向過程,而erlang則是天生面向進程,一切都是進程,甚至線程調度代碼都是自己寫的而不是直接將操作系統的API封裝一層,據說go的線程調度代碼只有幾百行,而erlang有幾萬行,這里就能看出差別了,甚至我一直覺得erlang完全可以自己單獨出來做操作系統
3.強制性的編碼風格
go也有缺點,比如強制性的一些編碼手段,如果你一個變量不使用,不會給你一個警告,而是會給你一個錯誤,無法通過編譯,據說是為了增加編譯速度,這個讓我很詫異,因為我在編程中經常需要預留接口或者變量,這個對於以后更改很有好處,在這種地方節省編譯時間有效嗎??我估計是go的設計者為了強迫大家寫出好看的代碼,這個在很多無法通過編譯的選項就可以看出,go對程序員的編程風格強制要求讓我很不適應...況且我們工程十多萬行代碼,整個項目重編,10分鍾和20分鍾有區別嗎??我想大部分人都是切出去做其他的事情
4.詭異的語法
var n int;這個是不是很詭異??反正我非常不習慣這個聲明變量的方式,還有大括號
1 if true { 2 //code 3 }else if false{ 4 //code 5 }else 6 { 7 //code 8 }
需要強制把大括號跟條件語句在一起,不知道go的設計師是為了標新立異還是懶得寫語法分析??這樣代碼反而沒有c的把大括號另起一行來的好看,不知道為什么要強制程序員這樣寫,不過讓我想起當初python的縮進也引起很多爭議...當初在論壇上還跟人爭辯過這個
5.編譯真的比C/C++快??
網上有人說go的編譯速度比C和C++快,go為了增加編譯速度強制程序員不得在代碼中有不使用的變量,import的包如果不使用也會被提示編譯錯誤,但這真的能解讓go編譯速度比C++快??C++因為語法復雜對於編譯器的設計者一直是個噩夢,但是我想go沒有lib或者obj的概念(我沒找到生成的臨時文件在哪里,還是說我的生成方式不對??),那我們在build一個項目的時候是不是要把所有的被import的文件重新編譯一遍??如果項目大起來編譯速度真的會比C++還快??如果C++的整個項目不重編的話go的編譯速度會比C++更有優勢??我記得網上看有人說C++的編譯方式會慢是因為一個文件一旦被include 46次,那么他也需要被打開46次,但我想這個問題go應該也存在,或者已經解決了??