對於靜態類型語言,其本質目標在於恰當地操做數據,獲得指望的值。具體而言,須要:ios
(1)定義數據類型數組
你定義的數據是什麼,是整形仍是浮點仍是字符。該類型的數據能夠包含的值的範圍是什麼。函數
(2)定義操做的含義指針
操做是嚴格數據類型相關的。操做代表了對了一個具備特定類型的數據,執行操做後產生什麼樣結果。 對象
===========================================blog
C++就是一個典型的靜態類型語言。在C++中,不管是"數據類型"仍是"操做",都分爲內置的和自定義的。繼承
C++的內置數據類型包括:編譯器
(1)基本內置類型string
整形、浮點、布爾、字符....io
(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++語言很是廣泛的操做。
該如何理解這樣的操做呢?舉個例子,例如:
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:基於該觀點理解重載
函數重載、操做符重載的本質,是用同一個名字定義了多個操做。結果是在編譯階段引入了一個肯定具體操做的過程 - 從候選操做中選出最匹配的操做。而上述「類型轉換」操做則是在運行階段進行的。
自定義操做,包括咱們定義的普通函數、成員函數、重載的操做符、構造函數定義的隱式類型轉換、拷貝構造函數定義的拷貝操做...
未完待續