程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++語言體系設計哲學的一些隨想(未完待續)

C++語言體系設計哲學的一些隨想(未完待續)

編輯:C++入門知識

C++語言體系設計哲學的一些隨想(未完待續)


  對於靜態類型語言,其本質目標在於恰當地操作數據,得到期望的值。具體而言,需要:   (1)定義數據類型   你定義的數據是什麼,是整形還是浮點還是字符。該類型的數據可以包含的值的范圍是什麼。   (2)定義操作的含義   操作是嚴格數據類型相關的。操作表明了對了一個具有特定類型的數據,執行操作後產生什麼樣結果。        ===========================================       C++就是一個典型的靜態類型語言。在C++中,無論是"數據類型"還是"操作",都分為內置的和自定義的。   C++的內置數據類型包括:   (1)基本內置類型   整形、浮點、布爾、字符....   (2)STL庫定義的類型   例如常用的iostream、string、迭代器......       此外C++和定義了復合類型機制,包括所有類型的引用、指針、數組,他們可以作為一個完整數據類型的一部分。   順便提一下,頂層/底層const、static、volatile...等修飾符,定義了數據的其他屬性,這些屬性也可以是一個完整數據類型的組成部分。       而自定義類型,最常用的就是class、struct、union定義,還有函數簽名,當然也可以使用復合類型機制定義自己類的引用、指針、數組等。       ==============================        重點在於,無論是變量還是常量,必須屬於某一特定的數據類型。因為操作只有基於精確的數據類型,其定義才有了確定的含義(在編譯原理中叫做“語義”)。也就是說,在一個確定的操作集合中(例如C++語言內置的所有操作),只要給一個變量賦於了數據類型,這個變量可以執行的操作也就確定了。定義變量nVal為int類型,那麼nVal就可以參與加減乘除、關系運算、拷貝、轉換為double、傳遞給函數形參、作為數組的下標.........       C++的“操作”,其含義非常廣泛。其實C++語言已經通過成員函數、操作符重載、函數重載、構造函數定義的隱式類型轉換...等機制,表明了 C++作為一個靜態類型語言的本質:屬於特定類型的數據,加上其上的操作。可以這樣理解,任何一個操作,本質就是函數,操作符在C++語言內部也是被當作函數來看待的(這也能解釋C++提供operator操作符重載機制的動機);類的成員函數、友元函數,也是對類本身這個“數據類型”的操作。   更進一步,操作本身也是一種特殊的數據類型。可以定義函數的指針、函數的數組,成員訪問(->*,.*),只是可以被當作數據類型來使用的機會不多,也被語言本身限制了。       C++的內置操作不太好理解,實際上我們常用的語言機制都是“操作”,具體包含了:   (1)各種各樣的操作符   算術操作符、關系操作符、位運算、取地址、單目運算、解引用、數組元素訪問.....        (2)拷貝操作   拷貝初始化、列表初始化(C++ 11)、賦值運算、函數傳參、函數返回值、類型轉換執行的臨時變量拷貝......等其他非引用場景       (3)數據類型轉換   類型轉換也是一種操作。對於普通的操作,執行前需先匹配要操作的數據的類型。現實中,不可能總能保證在代碼裡提供類型嚴格匹配的數據,因此類型轉換也是C++語言非常普遍的操作。   該如何理解這樣的操作呢?舉個例子,例如:    1 2 3 4 5 6 7 int nVal = 42;   double fVal = 3.14;   double fValTwo;   fValTwo = fVal + nVal ; // nVal類型提升為double  上述代碼最後一行的相加操作將執行類型提升。從編譯器的角度看,此時將生成一個匿名的變量,變量的類新和需要匹配的類型(double)相同,之後執行int至double的類型轉換操作,操作結果保存在這個匿名變量中。之後才會執行“+”操作。也就是說,如果選定了操作,那麼就會期待若干數據類型完全匹配的操作數,為了滿足這個條件,系統會執行類型轉換。   對於賦值操作,該操作會期待=右邊操作數的數據類型和左邊完全匹配,此時也會和上述相同,生成匿名變量,執行類型轉換。准備工作完成後,再執行"="操作。   函數的調用也是基於相同的原理,即實參類型和形參類型的匹配。   ... ...   C++語言內部定義了異常復雜的類型轉換規則(操作),只不過大多數對使用者是透明的。例如:   整形提升 - char、short、bool會先轉換為int;   類型提升 - 防止精度損失;   類型降低 -有精度損失,常見於拷貝操作。拷貝操作是將源對象嚴格匹配目標對象,因此不會有算術操作裡的“整形提升”。拷貝包括了拷貝初始化、賦值運算、函數調用實參賦給形參   非bool值都可以轉換為bool,相反則轉換為0/1;   任意類新指針都可轉換為void*;   數組在不用於decltype、sizeof、typeid、取地址&的情況下,會自動轉換為指向第一個元素的指針。   非底層const向底層const的轉換 - 指向常量的引用和指針可以綁定到非常量上,和內置類型的提升與降低不同,底層const向非底層const的轉換是非法的;   子類向基類的轉換 - 基類指針/引用可以指向子類,這是多態的基礎。和底層const一樣,相反的轉換是非法的。   ... ...   -   PS:關於底層const和繼承體系類型轉換的單向性:   本質而言,一個數據的數據類型,可以執行的操作的集合越小,該數據可以引用/綁定的對象類型越廣。例如:   數據類型A,可以執行operA - operZ 共26個操作。數據類新B,可以執行的操作是A的子集,比如operH-operN。那麼,B的引用/指針可以綁定到A(B的引用/指針可以接受A/A的指針賦值),相反則是非法的。   const int *不能修改指向的int,而int *可以,也就是說,數據類型const int *的操作范圍比int *要小,所以const int *可以綁定到int*指向的對象(本質上是指const int *可以接受int*賦值)。 在繼承體系中,基類的操作范圍肯定是小於子類的,所以 基類指針指向/基類引用 子類的合法的。   造成這一切的原因就在於,對靜態類型語言,編譯器始終“固執”地、“自以為是”地按照其靜態聲明類型,來決定一個操作是否合法,而不去管這個對象實際指向的類型。可以想象,編譯器“自以為是”地認為通過int *可以改變這個int,而不管這個int*實際指向的是const int,如果允許底層const向非底層const轉換,就會帶來沖突。   -     PS:基於該觀點理解重載   函數重載、操作符重載的本質,是用同一個名字定義了多個操作。結果是在編譯階段引入了一個確定具體操作的過程 - 從候選操作中選出最匹配的操作。而上述“類型轉換”操作則是在運行階段進行的。        

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved