/ Forside / Teknologi / Udvikling / C/C++ / Nyhedsindlæg
Login
Glemt dit kodeord?
Brugernavn

Kodeord


Reklame
Top 10 brugere
C/C++
#NavnPoint
BertelBra.. 2425
pmbruun 695
Master_of.. 501
jdjespers.. 500
kyllekylle 500
Bech_bb 500
scootergr.. 300
gibson 300
molokyle 287
10  strarup 270
Destructor
Fra : Riverwind


Dato : 16-07-02 16:30

Hejsa

Jeg har et problem, som jeg håber I kan hjælpe med.
Jeg bruger Visual Studio 6.0.

Jeg har en hjemmelavet messagethread, som jeg bruger som base for flere
nedarvede tråde.
Basetråden introducerer en virtuel metode - MessageHandler(..) - som de
nedarvede klasser overskriver.
Jeg vil gerne have, at tråden automatisk stopper, når tråd-objektet
bliver slettet. Derfor har jeg en boolean member-variabel -
m_bTerminate - som der bliver testet på i hvert gennemløb af
message-loop'et i tråden.
Det er mig bekendt fatalt at kalde virtuelle metoder, efter at
destructoren er blevet kaldt.
Lige nu bliver m_bTerminate sat til TRUE i baseklassens destructor, men
det er jo for sent, da MessageHandler(..) derved kan blive kaldt efter
at de nedarvede's destructor'er er blevet kaldt.

Jeg har prøvet at løse det ved at overskrive operator delete for min
basethread, men det kunne ikke kompilere.

Jeg vil helst undgå at skulle sætte variablen i de nedarvede klasser,
ligesom jeg vil undgå at skulle sætte den, der hvor tråd-objekterne er
instantieret.

Nogen gode råd til hvordan jeg kan sætte variablen inden destructoren
går igang?

/Riverwind



 
 
Mogens Hansen (16-07-2002)
Kommentar
Fra : Mogens Hansen


Dato : 16-07-02 18:53


"Riverwind" <riverwind@heaven.dk> wrote

[snip]
> Det er mig bekendt fatalt at kalde virtuelle metoder, efter at
> destructoren er blevet kaldt.

Absolut ja.

> Jeg har prøvet at løse det ved at overskrive operator delete for min
> basethread, men det kunne ikke kompilere.

Det ville også kun virke for heap-allokerede objekter.

> Jeg vil helst undgå at skulle sætte variablen i de nedarvede klasser,
> ligesom jeg vil undgå at skulle sætte den, der hvor tråd-objekterne er
> instantieret.

Det kan jeg godt forstå.
Det er et alvorligt problem, med en interessant løsning:
I stedet for at applikationsklassen arver fra MessageThread, så arver
MessageThread fra applikationsklassen.
Det er givetvis overraskende, hvis du ikke har set det før, men tænk
grundigt over det.

> Nogen gode råd til hvordan jeg kan sætte variablen inden destructoren
> går igang?

Det er ikke vigtigt at variablen sættes _før_ destructoren påbegyndes.
Det er derimod vigtigt at den virtuelle metode ikke kaldes _efter_
destructoren er færdig.

Jeg antager at destructoren kaldes fra en anden tråd end sig selv.
Den rene løsning er derfor at klassen f.eks. har et event objekt (oprettet
med Win32 API funktionen "CreateEvent"). I destructoren skal flaget sættes,
og derefter ventes der på at der bliver signaleret på eventen. I tråden
tester man på flaget, og afslutter når message-loop'et og signalerer på
eventen.



(Ikke testet skitse: jeg har ikke tid til alle detajlerne lige nu, men
syntes det er vigtigt nok til at give et ufuldstændigt svar)

template <typename T>
class MessageThread : public T
{
public:
~MessageThread();

// Note no virtual MessageHandler !!!
private:
HANDLE m_TerminateEvent;
};

MessageThread:MessageThread()
{
m_bTerminate = TRUE; // Win32 BOOL type ?!?
// Wait for thread to terminate
WaitForSingleObject(m_TerminateEvent, INFINITE);
}

void Message::Thread::MessageLoop()
{
while(!m_bTerminate) {
// ...
// MessageHandler();
}
SetEvent(m_TerminateEvent);
}

class MyHandler
{
public:
// Note MessageHandler is _not_ virtual
void MessageHandler();
};

int main()
{
MessageThread<MyHandler> mh;
}

Venlig hilsen

Mogens Hansen







Riverwind (16-07-2002)
Kommentar
Fra : Riverwind


Dato : 16-07-02 19:20


"Mogens Hansen" <mogens_h@dk-online.dk> wrote in message
news:ah1m7f$g9m$1@news.cybercity.dk...
[-=-]
> Det ville også kun virke for heap-allokerede objekter.
[-=-]
Det havde jeg overhovedet ikke tænkt på. Doh!

[-=-]
> Det er ikke vigtigt at variablen sættes _før_ destructoren påbegyndes.
> Det er derimod vigtigt at den virtuelle metode ikke kaldes _efter_
> destructoren er færdig.
[-=-]
Ja, men destructoren kører jo nedefra og op i arvehierakiet, så det er
nederste niveau, der ikke må være færdig, går jeg ud fra.

[-=-]
> Jeg antager at destructoren kaldes fra en anden tråd end sig selv.
> Den rene løsning er derfor at klassen f.eks. har et event objekt
(oprettet
> med Win32 API funktionen "CreateEvent"). I destructoren skal flaget
sættes,
> og derefter ventes der på at der bliver signaleret på eventen. I
tråden
> tester man på flaget, og afslutter når message-loop'et og signalerer

> eventen.
[-=-]
Ja, det er ikke tråden selv, der kalder destruktoren. Som du skriver, så
vil dette virke, når du laver tråd-klassen som en template, og lader
selve handleren ligge i sin egen klasse. Dvs. løsningen består faktisk i
at forhindre, at der overhovedet kaldes en virtuel metode.

Elegant løsning, som jeg nok lige skal overveje inden jeg giver mig i
kast med den. Det er vist temmelig mange ændringer, der skal laves (det
er en stor applikation).

Jeg har selv tænkt på denne løsning:

Destructoren laves protected i CMessageThread
Jeg laver en public metode:
void CMessageThread::Delete()
{
m_bTerminate = TRUE;
// Wait for thread to terminate
WaitForSingleObject(m_TerminateEvent, INFINITE);
delete this;
}

Det betyder så, at de kun kan oprettes på heap'en, men det tror jeg ikke
er noget problem.

Tak for svaret.

/Riverwind



Mogens Hansen (18-07-2002)
Kommentar
Fra : Mogens Hansen


Dato : 18-07-02 19:08


"Riverwind" <riverwind@heaven.dk> wrote

> "Mogens Hansen" <mogens_h@dk-online.dk> wrote

[snip]
> > Det er ikke vigtigt at variablen sættes _før_ destructoren påbegyndes.
> > Det er derimod vigtigt at den virtuelle metode ikke kaldes _efter_
> > destructoren er færdig.
> [-=-]
> Ja, men destructoren kører jo nedefra og op i arvehierakiet, så det er
> nederste niveau, der ikke må være færdig, går jeg ud fra.

Nemlig.

[snip]
> Dvs. løsningen består faktisk i
> at forhindre, at der overhovedet kaldes en virtuel metode.

Både og ...

Udgangspunktet for designet var egentlig ikke at undgå virtuelle metoder. Ej
heller var det målet at få lidt bedre performance. Det var rene design
overvejelser.
Det var væsentligt at objektet, hvor destructoren skal synkroniseres med
afslutning af tråden, skal destructoren køres som det sidste.

Virtuelle metoder har for alvor deres berettigelse, når der er brug for
polymophy på run-time.

Et typisk eksempel med behov for run-time polymorphy er et tegnings objekt,
som består af en række figurer (linier, cirkler etc.).
Indholdet i tegningen er run-time dynamisk - f.eks. bestemt interaktivt af
en bruger.
Tegningen tegner sig selv ved at bede de enkelte figurer om at tegne sig.

En anden almindelig anvendelse af virtuelle metoder, er at man har en
basisklasse, hvor man lader de afledte klasse afgøre den præcise opførsel
for dele af klassen, samtidig med at opførslen er konstant på run-time. Det
er egentlig er behov for, er compile-time polymorphy, for at opnå
kodegenbrug.

For den trådklasse du skitserede gælder det at for et givent objekt vil det
altid være den samme "MessageHandler" der bliver kaldt - altså ingen
polymorphy på runtime. "MessageHandler" var virtuel for at opnå kodegenbrug.

En god kilde til den slags overvejelser er bogen:
Multi-Paradigm DESIGN for C++
James O. Coplien
ISBN 0-201-82467-1

Det er iøvrigt nok et bedre design, hvis min MessageThread har et T objekt
som datamedlem i stedet for at arve fra T. Eventuelt kan den så have en
basis-klasse, hvorigennem tråden f.eks. kan suspendes og resumes.

>
> Elegant løsning, som jeg nok lige skal overveje inden jeg giver mig i
> kast med den. Det er vist temmelig mange ændringer, der skal laves (det
> er en stor applikation).

Du vil sikkert kunne nå langt med typedef's á la:
typedef MessageThread<MyHandler> MyThread;

Venlig hilsen

Mogens Hansen



Søg
Reklame
Statistik
Spørgsmål : 177558
Tips : 31968
Nyheder : 719565
Indlæg : 6408925
Brugere : 218888

Månedens bedste
Årets bedste
Sidste års bedste