賦值兼容規則(C++)

在一定條件下,不同類型的數據之間可以進行類型轉換,如可以將整型數據賦給雙精度型變量。在賦值之前,先把整型數據轉換成雙精度型數據,然後再把它賦給雙精度型變量。這種不同類型數據之間的自動轉換和賦值,稱爲賦值兼容。在基類和派生類對象之間也存有賦值兼容關係,基類和派生類對象之間的賦值兼容規則是指在需要基類對象的任何地方,都可以使用其子類對象來代替。

下面主要講積基類和派生類對象之間的賦值兼容
派生類的對象可以賦值給基類對象。

   A a1; //定義基類A對象a1
   B b1; //定義類A的公用派生類B的對象b1
   a1=b1; //用派生類B對象b1對基類對象a1賦值

  在賦值時捨棄派生類自己的成員,只進行數據成員的賦值。

  實際上,所謂賦值只是對數據成員賦值,對成員函數不存在賦值問題,內存中數據成員和成員函數是分開的。

 注意: 賦值後不能企圖通過對象a1去訪問派生類對象b1的成員,因爲b1的成員與a1的成員是不同的。  

假設age是派生類B中增加的公用數據成員,分析下面的用法:

  a1.age=23;//錯誤,a1中不包含派生類中增加的成員
b1.age=21; //正確,b1中包含派生類中增加的成員

只能用子類對象對其基類對象賦值,而不能用基類對象對其子類對象賦值,理由是顯然的,兩種對象的大小是不同的,因爲基類對象不包含派生類的成員無法對派生類的成員賦值。同理,同一基類的不同派生類對象之間也不能賦值
賦值兼容規則(C++)
2·派生類的對象可以初始化基類的引用。
已定義了基類A對象a1,可以定義a1的引用變量:

    A a1; //定義基類A對象a1
    B b1; //定義公用派生類B對象b1
    A &r=a1; //定義基類A對象的引用變量r(A的別名是r),並用a1對其初始化

這時,r和a1共享同一段存儲單元。也可以用派生類對象初始化引用變量r,將上面最後一行改爲

A& r=b1;//定義基類A對象的引用變量r,並用派生類B對象b1//對其初始化

注意: 此時r並不是b1的別名,也不與b1共享同一段存儲單元。它只是b1中基類部分的別名

這裏的r定義爲A類的引用,所以它的有效範圍就只有A類那麼大,r與b1中基類部分共享同一段存儲單元,r與b1具有相同的起始地址。 
如果函數的參數是基類對象或基類對象的引用,相應的實參可以用子類對象。
3·派生類對象的地址可以賦給指向基類的指針。也就是說,指向基類對象的指針變量也可以指向派生類對象。
例定義一個基類Student(學生),再定義Student類的公用派生類Graduate(研究生), 用指向基類對象的指針輸出數據。

#include <iostream>
#include <string>

using namespace std;
class Student//聲明Student類
{
   public :
   Student(int, string,float );//聲明構造函數
   void display( );//聲明輸出函數
   private :
   int num;
   string name;
   float score;
};
Student::Student(int n, string nam,float s)  //定義構造函數
{
   num=n;
   name=nam;
   score=s;
}
void Student::display( )//定義輸出函數
{
   cout<<endl<<″num:″<<num<<endl;
   cout<<″name:″<<name<<endl;
   cout<<″score:″<<score<<endl;
}
class Graduate:public Student//聲明公用派生類Graduate
{
   public :
   Graduate(int, string ,float ,float );//聲明構造函數
   void display( );//聲明輸出函數
   private :
   float pay;//工資
};
Graduate::Graduate(int n, string nam,float s,float p):Student(n,nam,s),pay(p){ }//定義構造函數
void Graduate::display() //定義輸出函數
{
   Student::display(); //調用Student類的display函數
   cout<<″pay=″<<pay<<endl;
}
int main()
{
   Student stud1(1001,″Li″,87.5); //定義Student類對象stud1
   Graduate grad1(2001,″Wang″,98.5,563.5); //定義Graduate類對象grad1
   Student *pt=&stud1;//定義指向Student類對象的指針並指向stud1
   pt->display( ); //調用stud1.display函數
   pt=&grad1; //指針指向grad1
   pt->display( ); //調用grad1.display函數
}

很多讀者會認爲: 在派生類中有兩個同名的display成員函數,根據同名隱藏的規則,被調用的應當是派生類Graduate對象的display函數,
在執行Graduate::display函數過程中調用Student::display函數,輸出num,name,score,然後再輸出pay的值。

事實上這種推論是錯誤的,先看看程序的輸出結果:

num:1001
name:Li
score:87.5
num:2001
name:wang
score:98.5
並沒有輸出pay的值。

問題在於pt是指向Student類對象的指針變量,它的指類是Student類,即使讓它指向了grad1,但實際上pt指向的是grad1中從基類繼承的部分(它指向的空間只能是基類中數據成員那麼大的空間)。通過指向基類對象的指針,只能訪問派生類中的基類成員,而不能訪問派生類增加的成員。所以pt->display()調用的不是派生類Graduate對象所增加的display函數,而是基類的display函數,所以只輸出研究生grad1的num,name,score3個數據。

其實,通過強制轉換也可以將Student類的地址賦值給Graduate類所定義的指針,但是,這樣做不安全,會讓使用者誤以爲可以調用Graduate類中增加的成員,其實不然,所以不建議使用

綜上所述,主要是因爲基類和派生類中成員所佔空間大小的不同,所引發的賦值兼容問題,例如int類型賦值給double類型,就是賦值兼容問題,而double類型賦值給int類型,就是不兼容,必須要強轉,不然會報錯

原文鏈接:https://blog.csdn.net/ilovekobemusic/article/details/8839371

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章