Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: osoittimella sohiminen virtuaalifunktioon ei onnistu !?

Sivun loppuun

t-rex [29.10.2008 20:39:56]

#

Pitäisi tehdä ikkuna luokka ja määritellä WM_PAINT ym. viestit kutsuttavaksi WndProc-aliohjelmasta virtuaalimetodeille.

eli:

LRESULT CALLBACK WndProcIkkuna(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
.
.
.
    case WM_PAINT:
       ClassIkkuna* ikkuna;
       ikkuna = (Ikkuna*) hWnd; // Tyyppimuunnos
       ikkuna->OnPaint(); // Virtuaalifunktio periytetyssä luokassa
       Break;
.
.
.

Kutsu ei kuitenkaan toimi! Mitä olen tehnyt väärin? OnPaint-metodi on määritelty virtuaaliseksi ja ClassIkkuna-luokasta on periytetty toinen luokka, jossa on määritelty uudestaan OnPaint-metodi.

hWnd osoitinhan sisältää luokan olion osoitteen ja virtuaalitaulukon pitäisi antaa aliluokan osoitin, mutta ko. kutsu saa aikaan vain ajon aikaisen virheen.
ikkuna->ClassIkkuna::OnPaint(); kyllä toimii, mutta ei kutsu aliluokan metodia, kuten virtuaalisen pitäisi.

APUVA!

Gaxx [29.10.2008 20:55:21]

#

Itselle ei tule yhtäkkiä mieleen, kuin kaksi mahdollisuutta.

Onhan parametrina välitetylle oliolle varattu muistia?

Kaatuuko ohjelma kutsuhetkellä, vai vasta OnPaint-metodin sisällä?

t-rex [29.10.2008 21:07:04]

#

Ohjelma kaatuu kutsuhetkellä. Olio on luotu osoittimen kautta new operaattorilla. Yritin vielä debugata ohjelmaa ja ikkuna-osoitin saa kyllä oikean osoitteen oliolle. En tarkemmin tiedä kuinka virtuaalinen kutsu oikeasti toimii, mutta käsittääkseni kääntäjä luo virtuaalitaulukon, jonka kautta osoittimia ohjataan oikeaan luokan metodiin.

Kutsut toimii, kun metodit ovat tavallisia. Ongelmat alkoivan, kun yritin luoda MFC- tyylisen virtuaalisen ikkunaluokan.

Gaxx [29.10.2008 21:54:47]

#

Tein vähän testejä ja itselläni tuollainen pelaa kuten pitääkin.

Jos vilautat luokkien esittelyistä oleelliset osat ja vaikka olioiden luonnin ja WndProc-funktion kutsumisen niin luulisi asian selkenevän.

koo [29.10.2008 21:56:03]

#

Tuo hWnd on Windowsissa kahva Windowsin ikkunarakenteeseen. Vaikka se C:ssä ja C++:ssa voi olla osoitintyyppinen, sitä ei pidä ajatella osoittimena. Sitten tuo ClassIkkuna on (ilmeisesti) koodaajan C++:ssa määrittelemä oma luokka, joka ei pohjimmiltaan ole mitään sukua sille Windowsin kahvalle. Pakotettu tyypinmuunnos muuntaa kahvan osoittimeksi ihan mukisematta, jolloin ikkunaolion osoitin osoittaa ihan vain jonnekin päin muistiavaruutta, josta ei varmaankaan löydy sitä C++:n ikkunaoliota. Tuo muunnos voisi onnistuakin, jos sopivat tyypinmuunnosoperaattorit tai -konstruktorit olisi määritelty, mutta enpä usko sen tässä mitenkään muuttavan perusongelmaa.

Siis lyhyesti: Vika on siinä, että hWnd ei muunnu osoittimeksi ClassIkkunaan eikä sen kuulukaan.

Asia voi olla kyllä toisinkin, mutta ensin pitäisi nähdä enemmän luokkaesittelyjä tms. koodia.

t-rex [29.10.2008 22:40:18]

#

Yliluokan esittely:

class Ikkuna
{
protected:
	HWND OmaWindowHandle;
public:
	Ikkuna(void);
	virtual ~Ikkuna(void); // virtuaalinen hajotin
	HWND Create( LPCTSTR WndClass, LPCTSTR WndName,int Style, int x, int y, int Width, int Height,HWND ParentWnd,HINSTANCE hinst);
	bool Show (int Style);
	void OnHScrollBarSize(double SBFlag);
	void OnVScrollBarSize(double SBFlag);
	virtual void OnPaint(); // Virtuaalinen ???
	void OnDestroy();
	void OnMouseMove(int MFlag, int x,int y);
};

NimiFrame, Periytetty luokka Ikkuna-luokasta:

class NimiFrame : public Ikkuna
{
public:
	NimiFrame(void);
	~NimiFrame(void);

	void OnPaint();
};

Esittelyt WinMain-aliohjelman alussa :

NimiFrame* INimiFrame = new NimiFrame ;

Windows välittää viestit oikealle aliohjelmalle Ikkuna-luokan LRESULT CALLBACK WndProcIkkuna(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

LRESULT CALLBACK WndProcIkkuna(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

	Ikkuna* ikkuna = 0;
	int PosX = 0,PosY = 0;

	switch (message)
	{
	case WM_PAINT:
	           ikkuna = (Ikkuna*) hWnd;
		   ikkuna->OnPaint();
	        break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}

	return 0;
}

koo [30.10.2008 01:14:56]

#

Virtuaalifunktio on ihan kunnossa. Mutta ajattelepa itsekin. Miten tällainen koodi (tiivistetysti) voi oikein mitenkään toimia?

NimiFrame *frame = new NimiFrame;
HWND hwnd = CreateWindow(...);
Ikkuna *ikkuna = (Ikkuna *) hwnd;

if (ikkuna == frame) {
  // Miten tänne voitaisiin ikinä päätyä?
}

Ikkuna ja HWND kun eivät ole mitään sukua toisilleen. Tuosta näkee, kuinka vaarallisia nuo pakotetut tyypinmuunnokset ovat, siis tuo (Ikkuna *) -tyylinen castaus. Kääntäjä ei pahemmin valita, kun sen käskee tukkia turpansa.

Ihan asian sivusta: C++-tyyliin ei tavata kirjoittaa Ikkuna(void);, kun pelkkä Ikkuna(); on oikeastaan selkeämpää. Vanhassa C:ssä oli toisin.

Gaxx [30.10.2008 08:26:03]

#

t-rex kirjoitti:

Yritin vielä debugata ohjelmaa ja ikkuna-osoitin saa kyllä oikean osoitteen oliolle.

Pitääkö tämä väite todella paikkansa?

Viimeisimmän viestisi perusteella se ei voi pitää paikkansa.

Luulin alkuun, että kyseessä on joku ns "simulointi" ja kutsut itse tuota WndProcIkkuna-funktiota.

Metabolix [30.10.2008 09:07:39]

#

Oliosi ensimmäinen jäsen on tuo HWND, ja en lainkaan epäile, etteikö sama HWND tulisi myös WndProcille. Se ei kuitenkaan ole osoitin luokkaasi. Vrt.

class C {
  int a;
};

void func(int a)
{
  C *c;
  c = (C*)a; // Mitä järkeä tässä voi koskaan olla?
}

t-rex [30.10.2008 13:13:41]

#

Oikeassa olette. Vietettyäni unettoman liskojen yön pohtiessani asiaa, valkeni asia kuin enkeli kolmelle tietäjälle. Eihän HWND ole tosiaan olion osoite vaan on jonkinlainen osoitin ikkunan tietoihin. Onko siis API:ssa funktiota, jolla saan selville ikkunan olion osoitteen, vai rakennanko oliotaulukon, jossa ikkunan kahva (HWND) toimii avaimena ? Vai onko jokin parempi ratkaisu ?

WndProc:han ei kuulu Ikkunaluokkaan vaan on oma aliohjelma, se ei siis periydy, enkä saa sitä kautta this-osoitinta.

koo kirjoitti:

Ihan asian sivusta: C++-tyyliin ei tavata kirjoittaa Ikkuna(void);, kun pelkkä Ikkuna(); on oikeastaan selkeämpää. Vanhassa C:ssä oli toisin.

voidit on kääntäjän rakentamia, itse en niitä yleensä käytä, C++:ssa kun ovat turhia.

Mod. korjasi lainauksen

Metabolix [30.10.2008 15:02:38]

#

Et saa olion osoitetta selville sen enempää kuin äskeisessä esimerkissäni voisit saada a:n osoitetta. Voit kuitenkin tehdä tietorakenteen, johon tallennat nuo. Esimerkiksi std::map sopii oikein hyvin, ulkomuistista ja testaamatta näin:

class Ikkuna {
  HWND hwnd;
  static std::map<HWND, Ikkuna*> ikkunat;
  void lisaa() { // kutsu, kun hwnd asetetaan
    ikkunat[hwnd] = this;
  }
  void poista() { // kutsu, kun hwnd tuhotaan (tai vaihdetaan)
    ikkunat.erase(hwnd);
  }
public:
  static Ikkuna* hae(HWND hwnd) {
    return ikkunat[hwnd];
  }
};
std::map<HWND, Ikkuna*> Ikkuna::ikkunat;

void funktio(HWND hwnd) {
  Ikkuna *ikkuna = Ikkuna::hae(hwnd);
}

t-rex [30.10.2008 16:39:26]

#

Hyvä. Tätä kaipasinkin !! Olisin luonut samantyyppisen oliolistan kuin tuo valmis std::map on, tosin olisin joutunut tekemään lisää työtä. Osoittimen saan tietenkin selville olion muodostimessa, josta olisin kutsunut listaolion metodia

Ikkuna* WndLista->Lisaa(this, HWND hWnd)

hWnd olisi avain this osoittimen hakemiseksi taulukosta. Tarkempi toteutus lienee sivuseikka.

Täytyy kokeilla...

kiitos.

t-rex [01.11.2008 20:36:55]

#

Ei tarvinnutkaan tehdä mitään taulukkoa olioille. WNDCLASS struktuuriin saa käyttäjä varata muistia. Käytin tätä ominaisuutta ja säilöin ikkunaluokan osoittimen sinne. Näin saan Ikkunan kahvan ja ikkunaolion osoitteen ilman ulkopuolisia mappeja tai listoja ym..
Kokeiltu on, ja osoittimet oikeita, virtuaalifunktiot toimii, kaikki hyvin. Ja onnistumisen ilo on ylimmillään (kop kop ...).

Kiitos asiallisista auttamisyrityksistä...


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta