【转】继承过程中 父类子类的 字段方法 内存分配 (非java语言)
名人名言:思想好比火星:一顆火星會點燃另一顆火星。一個深思熟慮的教師和班主任,總是力求在集體中創(chuàng)造一種共同熱愛科學和渴求知識的氣氛,使智力興趣成為一些線索,以其真摯的、復雜的關系——即思想的相互關系把一個個的學生連接在一起。——蘇霍姆林斯基?
首先給出部分代碼,由此來分析一下運行過程中對象、字段的創(chuàng)建過程,和編譯過程中方法列表的創(chuàng)建過程。
public class Animal
{
public virtual void Eat()
{
Console.WriteLine("Animal Eat" );
}
public virtual void Sleep()
{
Console.WriteLine("Animal Sleep" );
}
public void Play()
{
Console.WriteLine("Animal Play" );
}
}
public class Cat : Animal
{
public override void Eat()
{
base.Eat();//調(diào)用父類方法
Console.WriteLine("Cat Eat" );
}
public override void Sleep()
{
Console.WriteLine("Cat Sleep" );
base .Sleep();
}
}
?
以上代碼 如果在Main方法中 通過 Animal a = new Cat(); 來實現(xiàn)一個父類引用子類對象。
?
這句話首先是創(chuàng)建了一個Animal類型的a的引用,然后 new Cat();創(chuàng)建了一個Cat的對象,最后把這個a這個引用指向了
????????????? new Cat();這個對象的地址。在這個對象創(chuàng)建的過程中其實有很多步驟
?????????????
????????????? 首次訪問:(在此沒有顯示的寫出類中的構(gòu)造方法)
?????????????????? 順序:子類的靜態(tài)字段==》子類靜態(tài)構(gòu)造==》子類非靜態(tài)字段==》父類的靜態(tài)字段==》父類的靜態(tài)構(gòu)造==》父類的非靜態(tài)字段
???????????????????????????? ==》父類的構(gòu)造函數(shù)==》子類的構(gòu)造函數(shù)
?????????????????????????
????????????? 非首次訪問:順序是一樣的,只不過少了中間靜態(tài)字段和構(gòu)造的過程
??????????????
???????????? 這個過程依次類推直到遞歸到Object結(jié)束(在次過程中也是依次給父類分配內(nèi)存的過程),且字段的在內(nèi)存中的存儲順序是由上到下排列,object類的字段 排在最前面,
????????????? 原因是如果父類和子類出現(xiàn)了同名字段,則在子類對象創(chuàng)建時,編譯器會自動認為這是兩個不同的字段而加以區(qū)別。?
??????????????
????????????? 說了對象的創(chuàng)建,其次是方法列表的創(chuàng)建:
????????????? 方法列表的創(chuàng)建是在編譯時創(chuàng)建的,而對象的創(chuàng)建是在運行時,對象的創(chuàng)建是為了給方法列表一個引用的指針,使其它們動態(tài)關聯(lián)起來。
??????????????方法列表的創(chuàng)建順序跟字段的的順序是一樣的,也是先父類后子類。(override 和 new 的不同 new主要是會阻斷繼承樹,和隱藏父類方法,創(chuàng)建子類和父類同名的方法)
????????????? 父先子后的原因是:在編譯時創(chuàng)建方法列表的過程是,先生成父類的方法列表,而后在生成子類的方法列表的時候,會把父類的方法復制一份
????????????? 出來,然后拿子類的方法去和父類的比較,如果發(fā)現(xiàn)同名的方法,則看子類的方法修飾符是override 還是 new,如果是override 則覆蓋父類
????????????? 同名的方法(以上所說的父類方法皆是virtual方法,并且這里說的覆蓋只是說覆蓋方法的實現(xiàn),而并沒有覆蓋父類的方法列表,通過base.父類方法名還是可以調(diào)用父類的方法),
????????????? 如果是new? 則在內(nèi)存中的不同位置創(chuàng)建一個同名的方法。 不同名的,則直接創(chuàng)建。
?
???? 完成之后,我們可以通過 a這個引用來來調(diào)用Cat中的方法。
?
1.思考:如果把上例中Animal的play方法移到Cat中,在Main方法中打算通過a.Paly();來調(diào)用子類的Paly方法會發(fā)生什么現(xiàn)象? 會編譯不通過,為什么呢?
?
按理說子類就是用來擴展父類的,理論上也允許子類有自己的特性啊(方法、字段……)。但問題不是出在子類,而是出在了調(diào)用的位置,不能通過a.Play();來調(diào)用這個方法,可能大家又不解了,會想 a就是通過 new Cat();這個對象啊出來的,為什么不能調(diào)用自己的方法勒,一層層的,最終我們找到原因是在 Animal a 這個申明引用的位置。
?
在此要引入OO 的一個原則: 關注對象原則——調(diào)用子類還是父類的方法,取決于創(chuàng)建的對象是子類對象還是父類對象,而不是它的引用類型,而引用指針類型不同的區(qū)別決定了不同的對象在方法表中不同的訪問權(quán)限。
?
由此結(jié)合:子類可以調(diào)用父類方法和字段,而父類不能調(diào)用子類方法和字段? 這個概念。就可以知道原因了。
?
在說說OO中的另一個原則:就近原則——對于同名字段或者方法,編譯器是按照其順序查找來引用的,也就是首先訪問離它創(chuàng)建最近的字段或者方法。先貼一段代碼,然后通過代碼來分析。
?View Code?
?
class Program
{
static void Main(string [] args)
{
Animal a = new Cat();
// MemberInfo[] m = a.GetType().GetMembers();
// foreach (var item in m)
// {
// Console.WriteLine(item.Name);
// }
// a.Eat();
// a.Play();
//a.Sleep();
Console.WriteLine(a.AnimalName);
}
}
public class Animal
{
public string AnimalName = "Animal" ;
}
public class Cat : Animal
{
public string AnimalName = "Cat" ;
}
?
在代碼的Main方法中??Animal a = new Cat();? 這個A是Animal 類型的,結(jié)合文章開始將的 在編譯和運行過程中 子類、父類 的字段和方法以及實例化時候在內(nèi)存中分配的先后位置可以得出:Animal 類中的 AnimalName 在內(nèi)存中的位置一定位于 Cat中AnimalName在內(nèi)存中的位置的前面,根據(jù)就近原則打印出的應該是Animal。
?
以上文章大致概括了在繼承過程中的?在編譯和運行過程中 子類、父類 的字段和方法以及實例化時候在內(nèi)存中分配 和 執(zhí)行的先后,以及兩個原則,如有錯誤或者不足的地方請拍磚。后續(xù)后更深入的學習oo中的其它內(nèi)容。
總結(jié)
以上是生活随笔為你收集整理的【转】继承过程中 父类子类的 字段方法 内存分配 (非java语言)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 月薪6000存钱技巧!教你如何告别死工资
- 下一篇: 【转】多线程之有状态对象和无状态对象