本文主要講兩個問題:
(1) 為什麼我們在編寫代碼的時候,有時候強制轉換能成功,有些類型卻不可以? 這是C++核心規則 還是 使用者自訂規則?
(2)怎麼使用 核心控制的 整數或者字串明值來初始化自訂類對象? 比如 self_defined_class a=100; self_defined_class a="123"; 而不是 self_defined_class a(123) 或者 self_defined_class a(“123”);
第一個問題的答案是: 強制類型轉化 的規則是核心定義+使用者自訂的,也就是部分規則是核心定義,部分支援擴充,而不是核心限死的規則。
兩個類型對象是否能夠強制轉換,跟對象大小沒有絕對的關係。
【規則1】預設規則中,只有大對象向小對象轉換,就像子類對象能賦值給父類對象,但是父類對象無法賦值給子類對象 。例如:
class A{public:int a;} ;class B:public A{ public: int b;} ;
對於這樣的一個類,以下寫法正確:
A aa; B bb; aa=bb;
但是如果反過來賦值就是錯誤的,以下寫法錯誤:
A aa; B bb; bb=aa;
如果一定要用父類來初始化子類對象呢?? 其實 也可以,而且很簡單。只需要給B添加個建構函式,如下:
class A{public:int a;} ;class B:public A{ public: int b; B(){b=0;} B(A aa){b=aa.a;a=aa.a;}} ;
這樣定義之後,上述兩種寫法都正確。
我們可以這樣理解: C++ 核心規則為什麼只實現了子類對象向父類對象的賦值,而沒有實現父類對象像子類對象賦值?? 答案是:我們可以這樣理解,C++把使用者不能做的,在核心規則中以另一種方式給做了。我們可以想象,如果C++核心 不實現子類對象向父類對象賦值,那麼再牛逼的程式員也無法用C++實現。我們可以看下如果 C++不實現,那麼我們應當用以下代碼來實現:
class A{public:
A(B b){a=b.a;}int a;} ;class B:public A{ public: int b; B(){b=0;} B(A aa){b=aa.a;a=aa.a;}} ;
而,顯而易見的是,這代碼是不可能的,因為B 是A的衍生類別,定義必須在A之後,而定義A的時候還沒有B,又哪來的A(B b){a=b.a;}呢?
由此我們可以看出C++核心規則的精鍊,給程式員最大的自由。規則用於解決一些矛盾,而不會限制別人。
這裡我們再討論一個知識點,那就是:為什麼需要 拷貝建構函式?
C++設計者在初期的時候,要實現同類對象之間可以相互賦值。根據以上的分析,如果使用普通的建構函式如
B(B b){ ...}
因為b也是個B類執行個體,臨時變數用來接受傳入的參數,會在申明執行個體時同時產生,這樣的結果是導致無限迴圈。所以聰明的設計者們發明了 Copy Constructor
B(B&b){....}
這樣以來,傳值方式改為傳名, b對象的類型的實質變成了 B& 而不是B, 這樣也不會產生臨時物件變數。也不會觸發新的建構函式了。
預設的:
【規則2】 C++會為每個自訂類預設的添加拷貝建構函式,從而支援同類型對象之間的相互轉換。
(2) 在知道了以上知識後,我相信,問題二很簡單,只需要為你的類添加一個建構函式,就可以把C++核心變數int ,float,string等等直接 用等號= 來賦值,這是很酷的一件事。
那麼為何 用 = 號 就能夠觸發建構函式呢? 畢竟沒有申明新的類型對象啊??
答案是C++核心的又一條規則:
【規則3】C++會為每個自訂類預設的添加一個 “等號=”重載函數, 用於同類型對象之間的相互等號賦值。
=號重載函數的參數是右值,應當是一個const B& 或者B 變數,在賦值運算前,會首先執行強制轉換,如果成功,那麼賦值,如果沒定義,那麼編譯不通過。
通過今天的討論,我們知道,其實C++的核心規則 的核心部分很簡單,就是基本文法+ 基本類型。 其他的規則都是用來給這個語言系統打補丁,用來解決各種矛盾(或者叫做問題),我把它叫做“支撐規則”。所以,如果有一本C++的書,能夠不要從繁瑣的“支撐規則”說起,而是從最簡單的語句開始,一個個的解釋為什麼要有這條規則。我相信,C++會變得不那麼神秘。