Кратко съдържание



страница31/73
Дата21.07.2018
Размер9.03 Mb.
#76887
1   ...   27   28   29   30   31   32   33   34   ...   73

Виртуални членове


В дефинициите на членовете в горните примери забелязваме употребата на запазената дума override. Без нея те не биха могли да бъдат компили­рани. Това е така, защото въпросните членове са виртуални.

Виртуалните членове са един по-особен вид членове, без които по­ли­мор­физ­мът би бил неосъществим. Тяхната особеност проличава при нас­ле­дя­ва­не – на наследяващите класове се дава възможност вместо изцяло да пре­създадат даден наследен виртуален метод, просто да предоста­вят своя имплементация на същия. Така, ако работим с обект от наследе­ния клас през референция към базовия, той ще разполага с имплемента­циите, които наследникът е предоставил. Ще си изясним този механизъм при разглеждането на предефиниране и скриване на виртуални членове.

Виртуални членове се дефинират, като в дефиницията им се укаже ключо­вата дума virtual. Всички абстрактни членове, включително и тези, дефинирани в интерфейсите (и те са абстрактни, тъй като нямат импле­ментация), са винаги виртуални. Поради тази причина в някои обектно-ориентирани езици за програмиране (например в C++) абстрактните чле­нове се наричат още "чисто виртуални".

Предефиниране и скриване


При дефиниране на виртуален член в тип-наследник, чиято сигнатура съвпада с член, дефиниран в някои от базовите типове, той може или да се предефинира (да му се даде нова имплементация) или да се "скрие".

Когато се използва ключовата дума override, се реализира предефини­ране на виртуал­ния член, а когато се използва ключовата дума newскриване, което е и опцията, която се подразбира когато не се укаже никаква ключова дума.





Когато в наследен клас се предефинира виртуален член на базовия, този член е виртуален и в наследения клас.

Най-лесно ще доловим разликата между скриването и предефинирането на членове, като първо обърнем внимание на следната модификация на по-горния пример. В нея вместо абстрактен сме използвали нормален, конкретен клас, който съдържа дефиниции на свойствата, връщащи под­разбиращи се стойности и вместо override сме използвали new:

NonAbstractTest.cs

public class Car

{

public virtual int TopSpeed



{

// Retrieve the top speed in Kmph

get

{

return -1; // Default value



}

}

public virtual string BrandName



{

get


{

return "unknown"; // Default value

}

}

}


public class Trabant : Car

{

new public int TopSpeed



{

get


{

return 120;

}

}
new public string BrandName



{

get


{

return "Trabant";

}

}

}


public class Porsche : Car

{

new public int TopSpeed



{

get


{

return 250;

}

}
new public string BrandName



{

get


{

return "Porsche";

}

}

}


public class NonAbstractTest

{

static void Main()



{

Car[] cars = new Car[] {new Trabant(), new Porsche()};

foreach (Car car in cars)

{

Console.WriteLine("A {0} can go {1} Kmph",



car.BrandName, car.TopSpeed);

}

}



}

При изпълнението на този код получаваме следния, донякъде разочаро­ващ, рез­ул­тат:

A unknown can go -1 Kmph

A unknown can go -1 Kmph

Причината резултатът да се разминава с очакванията ни е, че при скриването на членовете наследяващият клас не предоставя своята де­фи­ни­ция на базовия. Така, когато достъпваме обект от наследен клас през ре­ференция към обект от базовия, разполагаме само с неговите собствени ре­ализации (на базовия клас). Поради това не можем да използваме полиморфи­зъм – когато достъпваме обект от базов клас, независимо от специфич­ният му тип, винаги ще ползваме имплементацията, дефинирана в ба­зо­вия, т. е. той може приеме само една форма.

Трябва да отбележим, че ако пропуснем запазената дума new, поведе­нието на кода ще бъде същото, но ще получим предупреждение от компи­латора "The keyword new is required on '' because it hides inherited member".

Ако в горния пример заменим new с override, ще задействаме механизма на полиморфизма и резултатът ще бъде следния:

A Trabant can go 120 Kmph

A Porsche can go 250 Kmph

Ако в горния пример пропуснем да обявим членовете TopSpeed и BrandName като виртуални, ще получим същия разочароващ резултат, както и преди:

A unknown can go -1 Kmph

A unknown can go -1 Kmph

Виждаме, че при използването на полиморфизъм има много варианти да сбъркаме и да получим неправилно поведение. Затова можем да запомним следното правило:



За да действа полиморфизмът, трябва полиморфният метод в базовия тип да е виртуален (да е обявен като virtual, abstract или да е член на интерфейс) и в класа наследник да е имплементиран с override.




Сподели с приятели:
1   ...   27   28   29   30   31   32   33   34   ...   73




©obuch.info 2024
отнасят до администрацията

    Начална страница