PDA

Tam Sürümünü Görmek İçin : durumu kurtarabilir miyiz?


atg
23/01/2006, 15:38
class a
{
public:
virtual void func_a(string& param)=0;//parametre gidince
//sorunlarda gidiyor aslında
};
class b
{
public:
virtual void func_b()=0;
};

class c
{
b* m_pb;
public:
c(b* pb)
{
m_pb = pb;
}
void z()
{
a* pa = dynamic_cast<a*>(m_pb);//nanay: null pointer;
a* pa2 = reinterpret_cast<a*>(m_pb);

//pa->func_a();//denemiyorum bile

pa2->func_a(string());//hata: Calling Convention
}
};


class d : a, b
{
public:
void func_a(string& param){}
void func_b(){}
};

int main()
{
d o_d;
c o_c((b*)&o_d);

o_c.z();

return 0;
}


msvc 8, __cdecl, amd 32-bit,


acehreli
23/01/2006, 20:14
Hangi satirla ilgili olarak soruyorsun? Acaba b'yi a'dan turetmeyi mi unuttun; yoksa ilgisiz iki sinifi birbirine dOnUsturmekten bir sey anlamadim.

Ali

atg
23/01/2006, 21:07
Hata veren satırlardan herhangi biri hata vermeyi keserse bana yeter.

a ve b birer arayüz, d her ikisinede sahip, d sınıfı ,c sınıfının eline, b tipi arayüz olarak geçiyor, bende bunu cast kullanarak a tipi arayuze çevirmeye çalışıyorum, reinterpret_cast işi beceriyor ama a::func_a çalıştığı anda hata üretiyor, işin garip tarafı a::func_a nın aldığı parametreyi ortadan kaldırınca hatada kayboluyor, tamam başka yöntemlerlede problemi aşabilirim ama hatanın sebebini anlamaya çalışıyorum. -sanırım yine derin c++ araştırmalarına girecez-

Raiser
23/01/2006, 21:55
acehreli'nin dediği gibi birbiriyle ilgisiz iki nesne sınıfını dynamic_cast ile birbirine çevirmeye çalıştığından dolayı null pointer dönüyor büyük ihtimalle. dynamic_cast çalışma esnasında dönüştürme türleri arasında kontrol yapar, dönüştüremezse exception üretir.

Bahsettiğin calling convention hatası bende çıkmadı, msvc6

atg
23/01/2006, 23:27
Bir kere daha söylemiş olayım, hata program çalıştığında ortaya çıkıyor. Kodu gcc ile derledim herhangi bir hata vermiyor ve pa göstergeçine 0 değerini atıyor(ortada exception yok). Dikkat ettiğim diğer bir husus ise d::func_a işlevinin içeriğinde bulunabilecek olası kodlar çalıştırılmıyor, tabi durum vc6 için geçerli mi bilemiyorum.

acehreli
24/01/2006, 00:03
Raiser, dynamic_cast basarisizlik referans tUrune dOnusturulurken olusursa hata (exception) atar. Gosterge tUrUne donustururken hata atilmaz, atg'nin de gordugu gibi, null doner.

atg, daha iyi anladim; biraz daha bakayim...

Ali

acehreli
24/01/2006, 00:16
Eger dogru anladiysam, dynamic_cast'i once d* icin uygulaman gerekiyor. Aciklamalar kodun icinde...


#include <string>

using namespace std;

class a
{
public:

// [Ali] Burada parametrenin 'const' olmasi gerekiyor
// veya, asagida cagirdigin yerde gecici nesne
// kullanmaman gerekiyor.
virtual void func_a(string const & param)=0;//parametre gidince
//sorunlarda gidiyor aslinda
};
class b
{
public:
virtual void func_b()=0;
};

// [Ali] Biraz asagida kullanacagim icin, d'nin tanimini buraya
// tasidim.
//
// Ayrica burada kalitimin 'public' olmasi gerekiyor tabii ki
class d : public a, public b
{
public:

// [Ali] Ayni 'const'i burada da kullaniyorum
void func_a(string const & param){}
void func_b(){}
};


class c
{
b* m_pb;
public:
c(b* pb)
{
m_pb = pb;
}
void z()
{
// [Ali] Buradaki sorun su: Her b bir d olmayabilir.
// Elimizde bir b* var ama bu, bambaska bir turun b
// parcasini gosteriyor olabilir.
//
// Onun icin, oncelikle bu b'nin bir d olup olmadigina
// bakmamiz gerekiyor:

d * pd = dynamic_cast<d*>(m_pb);

if (pd)
{
// [Ali] Super! Artik bunun bir a arayuzu de oldugunu
// biliyorum. Bir a gibi kullanabilirim... Hem de
// hic tur donusumune gerek olmadan.
a * pa = pd;

pa->func_a(string());
}
}
};


int main()
{
d o_d;

// [Ali] Burada bir tUr donusumune gerek yok
// Zaten C-tUru parantezli tUr donusumunu hic
// kullanmamak gerek.
//
// Bence kalitimi public yapmadigin icin tUr donusumu
// gerekmisti...
c o_c(&o_d);

o_c.z();

return 0;
}


Ali

acehreli
24/01/2006, 00:18
Bir kac duzeltme yaptim. Umarim onu okumussunuzdur...
Ali

acehreli
24/01/2006, 01:02
Atladigim noktalara devam etmek istiyorum...

Visual Studio calisma zamaninda bir hata farkediyor (ESP'nin degerini bir yerde yanlis buluyor). Bunun "calling convention"larla ilgili bir sorun oldugunu tahmin ediyor; ama oyle oldugunu soylemiyor; yalnizca bir ipucu...

Bu hatayi doguran sey, kodda yanlis bir amac icin reinterpret_cast islecinin kullaniliyor olmasidir. Cunku o islec, tUr donusumu islecleri icinde guvenlik acisindan C'nin parantezli donusumunden ancak bir derece daha iyidir. Bakalim...

d'nin a ve b diye iki parcasi var. d'nin ayrica bu iki arayuzu dogru kullanabilmek icin tasidigi ne az iki tane vtable gostergesi var. Bir derleyici bu parcalari bir d icinde su sirada tutabilir:

a icin vtable gostergesi
a parcasi
b icin vtable gostergesi
b parcasi
d'nin diger ogeleri

m_pb, bir b gostergesi oldugu icin d'nin ortasinda bir yeri gosteriyor. Biz o gostergeyi alip reinterpret_cast yoluyla "aslinda sen sanki orada bir a varmis gibi" davran diyince de derleyici ornegin "b icin vtable gostergesi"ni "a icin vtable gostergesi" olarak kullaniyor. Iyi ki bir noktada yaptigi bir denetim sonucunda da hatayi farkediyor.

static_cast ve dynamic_cast ise guvenlidir cunku derleyici bu donusumleri yaparken gereken adres hesabini da yapar ve dogru alt nesneye gosterge dOndUrur.

Sinif siralamasinda yaptigim degisiklikte de bir sakinca gormuyorum. Cunku aslinda basliklara yalnizca siniflari tanimlarini koyacagiz, islevlerin tanimlarini ise gercekleme dosyalarinda yapacagiz degil mi... Ben soyle goruyorum:

a.h ve b.h hicbir baslik #include etmeyecekler.

c.h soyle olacak:


// Onbildirim:
class b;

class c
{
b* m_pb;
public:

// [Ali] buraya bir explicit eklemek de iyi olur:
explicit c(b* pb);

void z();
};


c.cpp ise artik diger siniflari kullandigi icin #include etmek zorunda:


#include "a.h"
#include "b.h"
#include "d.h"

c::c(b * pb)
:
m_pb(pb) // <-- [Ali] Islev icinde atamadan daha iyi
{}

void c::z()
{
/* ... */
}


Eger d de c'yi kullaniyorsa, o da benzer sekilde yalnizca d.cpp icinde #include "c.h" yapacak.

Ali

Raiser
24/01/2006, 12:55
class iface{
public:
virtual int x()=0;
};

class iface2{
public:
virtual int y()=0;
};

class mert : public iface{
public:
int x() { return 0;}
int a;
};

class dmert : public iface2{
public:
int y() { return 0; }
int b;
};

//

int main()
{
mert* p_m = new mert;
dmert* p_dm = new dmert;

try
{
p_m = dynamic_cast<mert*>(p_dm);
}
catch(...)
{
printf("eksepsin");
}
return 0;
}


Bu örnek kodda dynamic_cast'i pointer ile kullanırken msvc6 exception üretiyor ya da bende bir sorun var..

acehreli
24/01/2006, 19:00
VC++'in bir hatasi herhalde... Bu konulara derleyicilerin ne yaptiklarina bakarak karar vermek yetersiz kalir ama baska bir ornek olarak, g++ 3.4.2 beklendigi gibi, 0 dOnduruyor.

Surada VC++'in bu hatasina da deginen bir sohbet buldum:

http://groups.google.com/group/comp.lang.c++/browse_frm/thread/edcdf9b3e17cfedb/81e6a794c1388bde?lnk=st&q=failed+dynamic_cast+exception+msvc%2B%2B&rnum=1#81e6a794c1388bde

Orada birisi /GR seceneginin bu davranisla bir ilgisi olabilecegini soyluyor.

Konuyu baglamak icin, standardin "5.2.7 Dynamic cast" basliginin 9uncu paragrafi soyle diyor: "The value of a failed cast to pointer type is the null pointer value of the required result type. A failed cast to reference type throws bad_cast (18.5.2)."

Turkcesi kabaca: Basarisiz bir gosterge tUr dOnusumunun sonucu, sonuc tUrde null gosterge degeridir. Basarisiz bir referans tUr dOnusumu bad_cast atar (18.5.2).

Ali

gKuru
25/01/2006, 07:41
Orada birisi /GR seceneginin bu davranisla bir ilgisi olabilecegini soyluyor.Yep (/GR : Enable Run-Time Type Info).
Zaten Raiser ın kodunda fırlatılan exception std::bad_cast değil.
(
try..catch i kaldırıp debugger ile göz atarsak :

Unhandled exception at 0x7c81eb33 in bla_bla.exe: Microsoft C++ exception: std::__non_rtti_object at memory location 0x0013fef0..

)
Buradan da anlayacağımız üzere, run-time type info acık değil .

Raiser
31/01/2006, 00:30
Evet, exception'un tipine hiç bakmamıştım. Derleyiciye /GR parametresini verince olması gerektiği gibi null pointer döndürüyor, exception atmıyor..