C # ve Java uygulamalarında, nesneler genellikle sınıfında tek bir işaretçiye sahiptir. Bu mümkündür çünkü bunlar tek miras dilleridir. Sınıf yapısı daha sonra tek miras hiyerarşisi için vtable'ı içerir. Ancak, arabirim arama yöntemleri de çoklu kalıtımın tüm sorunlarına sahiptir. Bu tipik olarak, tüm uygulanmış arayüzler için sınıf yapısına ilave boşluklar koyarak çözülür. Bu, C ++ 'daki tipik sanal miras uygulamalarına kıyasla yer kazandırır, ancak arabirim yönteminin daha karmaşık olmasını sağlar; bu, önbelleğe alma ile kısmen telafi edilebilir.
Örneğin, OpenJDK JVM'de, her bir sınıf tüm uygulanan arayüzler için bir dizi değişken içerir (bir arayüz değişkenine bir değişken adı verilir ). Bir arabirim yöntemi çağrıldığında, bu dizi, bu arabirimin yinelemesi için doğrusal olarak aranır, daha sonra yöntem bu yinelemenin içinden gönderilebilir. Önbellekleme, her bir çağrı sitesinin yöntem gönderiminin sonucunu hatırlaması için kullanılır, bu nedenle bu arama yalnızca somut nesne türü değiştiğinde tekrarlanmalıdır. Yöntem gönderme için sözde kod:
// Dispatch SomeInterface.method
Method const* resolve_method(
Object const* instance, Klass const* interface, uint itable_slot) {
Klass const* klass = instance->klass;
for (Itable const* itable : klass->itables()) {
if (itable->klass() == interface)
return itable[itable_slot];
}
throw ...; // class does not implement required interface
}
(OpenJDK HotSpot yorumlayıcısındaki veya x86 derleyicisindeki gerçek kodu karşılaştırın .)
C # (veya daha doğrusu CLR) ilgili bir yaklaşım kullanır. Bununla birlikte, buradaki dökülenler yöntemlere işaretçiler içermezler, fakat slot haritalarıdırlar: sınıfın ana oyunda bulunan girişleri gösterirler. Java’da olduğu gibi, doğru itable’ın aranması sadece en kötü durum senaryosudur ve çağrı sitesinde önbelleğe almanın bu aramayı neredeyse her zaman önleyebileceği beklenir. CLR, JIT tarafından derlenen makine kodunu farklı önbellekleme stratejileriyle düzeltmek için Sanal Saplama Gönderme adlı bir teknik kullanır. pseudocode:
Method const* resolve_method(
Object const* instance, Klass const* interface, uint interface_slot) {
Klass const* klass = instance->klass;
// Walk all base classes to find slot map
for (Klass const* base = klass; base != nullptr; base = base->base()) {
// I think the CLR actually uses hash tables instead of a linear search
for (SlotMap const* slot_map : base->slot_maps()) {
if (slot_map->klass() == interface) {
uint vtable_slot = slot_map[interface_slot];
return klass->vtable[vtable_slot];
}
}
}
throw ...; // class does not implement required interface
}
OpenJDK-pseudocode'un temel farkı, OpenJDK'de her bir sınıfın doğrudan veya dolaylı olarak uygulanan tüm arayüzlerin bir dizisine sahip olmasıdır, CLR ise yalnızca bu sınıfta doğrudan uygulanan arayüzler için bir dizi slot haritasını tutar. Bu nedenle, miras hiyerarşisini bir yuva haritası bulunana kadar yukarıya doğru yürümemiz gerekir. Derin kalıtım hiyerarşileri için bu, yerden tasarruf sağlar. Bunlar, jenerik ilaçların nasıl uygulandığına bağlı olarak CLR ile ilgilidir: genel bir uzmanlık için, sınıf yapısı kopyalanır ve ana listedeki yöntemler uzmanlıklarla değiştirilebilir. Slot haritaları doğru oy girişlerini göstermeye devam eder ve bu nedenle bir sınıfın tüm genel uzmanlıkları arasında paylaşılabilir.
Bitiş notu olarak, arayüz gönderimini uygulamak için daha fazla olasılık vardır. Vtable / değişken tabloyu nesneye veya sınıf yapısına yerleştirmek yerine , temelde bir çift olan nesneye yağ işaretçileri kullanabiliriz (Object*, VTable*)
. Dezavantajı bunun işaretçilerin boyutunu iki katına çıkarması ve üst üste bindirmelerin (somut bir tipten bir arayüz tipine) serbest olmamasıdır. Ancak daha esnektir, daha az dolaylıdır ve ayrıca arayüzlerin bir sınıftan harici olarak uygulanabileceği anlamına gelir. İlgili yaklaşımlar Go arayüzleri, Rust özellikleri ve Haskell sınıfları tarafından kullanılır.
Kaynaklar ve daha fazla okuma:
- Wikipedia: Satır içi önbellekleme . Pahalı yöntem aramalarını önlemek için kullanılabilecek önbellek yaklaşımlarını tartışır. Tipik olarak vtable tabanlı sevkiyat için gerekli değildir, ancak yukarıdaki arayüz gönderme stratejileri gibi daha pahalı sevkiyat mekanizmaları için çok arzu edilir.
- OpenJDK Wiki (2013): Arayüz Çağrıları . Buzları tartışır.
- Pobar, Neward (2009): SSCLI 2.0 Internals. Kitabın 5. bölümü, slot haritalarını ayrıntılı olarak ele alıyor. Hiç bir zaman yayınlanmadı ancak yazarlar tarafından bloglarında yayınlandı . PDF bağlantı beri taşındı. Bu kitap muhtemelen CLR'nin şu anki durumunu yansıtmamaktadır.
- CoreCLR (2006): Sanal Saplama Gönderimi . In: Çalışma Zamanı Kitabı. Pahalı aramaları önlemek için slot haritalarını ve önbelleklemeyi tartışır.
- Kennedy, Syme (2001): .NET Ortak Dil Çalışma Zamanı için Jenerik Tasarım ve Uygulama . ( PDF bağlantısı ). Jenerikliği uygulamak için çeşitli yaklaşımları tartışır. Jenerikler metot gönderimi ile etkileşime girer, çünkü metotlar özelleştirilebilir, böylece vtables yeniden yazılabilir.