... daha iyi bir açıklama ya Meyer birinde orada bir yerde ya da Sutter'in kitapları, ama arama gibi hissetmedim. Gördüğünüzün, sanal fonksiyonların nasıl uygulandığı (vtables) ve "siz onu kullanana kadar bunun için ödeme yapmamanın" bir sonucu olduğunu düşünüyorum.
Kullanımda sanal yöntem yoksa, nesneye yönelik bir işaretçi nesnenin verilerine işaret eder. Sanal bir yöntem sunulduğunda, derleyici sanal bir arama tablosu (vtable) ekler ve işaretçi bunun yerine işaret eder. Muhtemelen bir şey kaçırıyorum (ve beynim henüz çalışmıyor) çünkü bunu temel sınıfa bir veri üyesi ekleyene kadar bunu gerçekleştiremedim. Temel sınıf bir veri üyesine sahipse ve birinci alt sınıf bir sanal içeriyorsa, ofsetler vtable boyutuna göre değişir (derleyicimde 4).İşte bu açıkça gösteren bir örneği: benim makinede bu Running
template <typename T>
void displayAddress(char const* meth, T const* ptr) {
std::printf("%s - this = %08lx\n", static_cast<unsigned long>(ptr));
std::printf("%s - typeid(T).name() %s\n", typeid(T).name());
std::printf("%s - typeid(*ptr).name() %s\n", typeid(*ptr).name());
}
struct A {
char byte;
void f() { displayAddress("A::f", this); }
};
struct B: A {
virtual void v() { displayAddress("B::v", this); }
virtual void x() { displayAddress("B::x", this); }
};
struct C: B {
virtual void v() { displayAddress("C::v", this); }
};
int main() {
A aObj;
B bObj;
C cObj;
std::printf("aObj:\n");
aObj.f();
std::printf("\nbObj:\n");
bObj.f();
bObj.v();
bObj.x();
std::printf("\ncObj:\n");
cObj.f();
cObj.v();
cObj.x();
return 0;
}
(MacBook Pro) aşağıdaki yazdırır:
aObj:
A::f - this = bffff93f
A::f - typeid(T)::name() = 1A
A::f - typeid(*ptr)::name() = 1A
bObj:
A::f - this = bffff938
A::f - typeid(T)::name() = 1A
A::f - typeid(*ptr)::name() = 1A
B::v - this = bffff934
B::v - typeid(T)::name() = 1B
B::v - typeid(*ptr)::name() = 1B
B::x - this = bffff934
B::x - typeid(T)::name() = 1B
B::x - typeid(*ptr)::name() = 1B
cObj:
A::f - this = bffff930
A::f - typeid(T)::name() = 1A
A::f - typeid(*ptr)::name() = 1A
C::v - this = bffff92c
C::v - typeid(T)::name() = 1C
C::v - typeid(*ptr)::name() = 1C
B::x - this = bffff92c
B::x - typeid(T)::name() = 1B
B::x - typeid(*ptr)::name() = 1C
ilginç şey olduğunu hem bObj
ve cObj
sergi arasındaki olarak adres değişikliği A
ve B
veya C
numaralı telefonlarda arama yöntemleri. Fark, B
'un bir sanal yöntem içermesidir. Bu, derleyicinin işlev sanallaştırmasını uygulamak için gerekli ek tabloyu eklemesine izin verir. Bu programın gösterdiği diğer ilginç şey, typeid(T)
ve typeid(*ptr)
'un hemen hemen çağrıldığında B::x
'da farklı olmasıdır. Ayrıca sanal tablo eklendiğinde, sizeof
kullanarak boyut artışını görebilirsiniz.
Sizin durumunuzda, CWaitable::WakeWaiters
sanal gerçekleştirdiğiniz anda, vtable eklenir ve aslında gerekli olan defter tutma yapılarının eklenmesiyle nesnenin gerçek tipine dikkat eder. Bu, nesnenin tabanına olan ofsetin farklı olmasına neden olur. Efsanevi bir bellek düzenini tanımlayan referansı ve bir nesnenin adresinin mirasın eğlenceye karıştığı zamanki yorumlanmasına neden bağlı olduğunu bulmayı gerçekten çok isterdim.
Genel Kural: (ve bu duymuş önce) baz sınıfları hep sanal yıkıcı var. Bu, böyle küçük sürprizleri ortadan kaldırmaya yardımcı olacaktır.
Dilimleme, nesneler üzerinde olur ve işaretçiler değildir. Gönderdiğiniz kod, küçük bir düzeltmeden sonra çalışır. Bazı gerçek kod gönderin - kristal topum bugün çalışmıyor. BTW: Gerçekten bunu 'bu' geçmeyi düşünmüyorsun, değil mi? – dirkgently
Bu neredeyse kesin olarak birden fazla kalıtımdır - kod örneği muhtemelen fazladan kırpılmıştır. –
@Earwicker: Birden fazla kalıtım nereden geldi? – dirkgently