/ 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
virtual metode kaldes ikke
Fra : Hans Willumsen


Dato : 25-01-03 12:45

Jeg har en klasse hvor en virtuel metode ikke kaldes som den skal.
Sagen er i store træk sådan :

class CParent
{
protected:
virtual void SpecifikInitialisering() = 0;
public:
CParent(int p1, int p2) { ..initialiser lidt her;
SpecifikInitialisering();}
};

class CChild
{
protected:
void SpecifikInitialisering() {gør noget specifikt;}
public:
CChild(int p1, int p2) : CParent(p1,p2) {gør intet;}
};

jeg forventer at når jeg laver en klasse af CChild så vil CParent
constructoren køre og kalde CChilds udgave af SpecifikInitialisering og
ikke sin egen. I praksis sker det ikke. Constructoren kalder parents
specifiikke init, som er abstrakt og det giver en fejl. Sjovt nok ikke en
compilerfejl men runtime "pure virtuel method called. pres ok to accept that
this sucks".

Bruger borland builder 6 hvis det skulle have noget at sige.

.....<5 minuter senere>... hmm.. kan det have noget at gøre med at CChild
delen ikke er lavet endnu i det CParent constructoren kalder
SpecifikInitialisering? Det kunne måske tænkes.
Hvis det er tilfældet, hvordan kommer man så mest elegant ud af det problem?
Jeg vil gerne kalde den specifikke initialisering i constructoren... er jeg
virkelig nødt til at kalde den explicit i CChilds constructor? og i alle
andre klasser som skal arve fra parent..?




 
 
Igor V. Rafienko (25-01-2003)
Kommentar
Fra : Igor V. Rafienko


Dato : 25-01-03 16:55

[ Hans Willumsen ]

[ ... ]

> jeg forventer at når jeg laver en klasse af CChild så vil CParent
> constructoren køre og kalde CChilds udgave af SpecifikInitialisering
> og ikke sin egen.
:
> Bruger borland builder 6 hvis det skulle have noget at sige.


Nei, det har ingenting å si, da Builder 6 oppfører seg korrekt.

C++ har (dessverre) gjort eget valg hva angår constructors og
virtuelle metoder -- C++ FAQ lite, 23.3.


> ....<5 minuter senere>... hmm.. kan det have noget at gøre med at
> CChild delen ikke er lavet endnu i det CParent constructoren kalder
> SpecifikInitialisering? Det kunne måske tænkes. Hvis det er
> tilfældet, hvordan kommer man så mest elegant ud af det problem? Jeg
> vil gerne kalde den specifikke initialisering i constructoren... er
> jeg virkelig nødt til at kalde den explicit i CChilds constructor?
> og i alle andre klasser som skal arve fra parent..?


Standard"trickset" å lage en virtuell "init"-metode, som man så kaller
eksplisitt (ja, det ser jævlig ut). Eller å bruke Factory-pattern,
slik at objektene skapes på en annen måte enn via den "vanlige"
definisjonen/new. FAQ'en til c.l.c++ har noen eksempler på disse
strategiene (og de får meg til å lure om ikke C++ er
eksistensgrunnlaget for GoF).





ivr
--
<peder> igorr: tcl ja... det er fra de dypeste avgrunnene i helvete det...
<peder> php er bare fra foajeen
            -- pederst på irc

Mads Orbesen Troest (25-01-2003)
Kommentar
Fra : Mads Orbesen Troest


Dato : 25-01-03 19:59

Hej Hans;

> jeg forventer at når jeg laver en klasse af CChild så vil CParent
> constructoren køre og kalde CChilds udgave af SpecifikInitialisering og
> ikke sin egen.

Det kan ikke lade sig gøre på den måde, og når man tænker lidt over det er
det klart nok: Eftersom specialiseringshierarkiet initialiseres oppefra og
ned har man af gode grunde ikke mulighed for at "kalde ned" i en videre
specialisering, idet den endnu ikke er initialiseret. Samme, blot med modsat
fortegn, gælder i destructors; du kan ikke "kalde ned" fra en base-class
destructor, idet de videre specialiseringer allerede er destrueret. Altså: i
ctors/dtors kører den sene binding ikke, og selv om de metoder man kalder er
erklæret virtuelle, så bliver det dem, der er implementeret på eget niveau,
der kaldes (dette er veldefineret; det er altså ikke forbudt at kalde
virtuelle metoder, de opfører sig bare som om de ikke var det).

Som jeg kan se Igor allerede har nævnt, er Factory Pattern'et en løsning.
Det er i forvejen en god idé at centralisere konstruktionen af objekter i en
factory, og så ellers udelukkende programmere op mod interfaces (der i C++
implementeres som rene abstrakte klasser); dermed reducerer du
afhængighederne i dit program (der ville ellers være en afhængighed til
realisationsklasserne hver gang operator new benyttes på en af dem). Når du
så i din factory har en "IWhatever *newWhatever( const char *inSomeArgPSZ )"
metode, så kalder dens implementation "new RWhatever( inSomeArgPSZ )" og
kalder dernæst en "initialise" metode på den nye instans, før den returneres
(i upcastet interface form, hvilket sker automatisk via den "covariante"
typekonversion, som det så fint hedder).

Med venlig hilsen,
/\/\\ads Orbesen Troest



Hans Willumsen (25-01-2003)
Kommentar
Fra : Hans Willumsen


Dato : 25-01-03 22:09

> Som jeg kan se Igor allerede har nævnt, er Factory Pattern'et en løsning.

Hm. I denne tråd? Ser ikke andre besværelser end din og mogens' som ikke
indeholder andet end citat?
Anyway, så er det naturligvis en god ide. Så kan factory, som du jo også
skriver, håndtere ekstra ting ud over hvad new vil gøre.
Er det normalt at lade factory være friend af objektet som den laver?
Umidelbart virker det forkert for mig at have en initialiseringsroutine
liggende public.

> Det er i forvejen en god idé at centralisere konstruktionen af objekter i
en
> factory, og så ellers udelukkende programmere op mod interfaces (der i C++
> implementeres som rene abstrakte klasser); dermed reducerer du
> afhængighederne i dit program (der ville ellers være en afhængighed til
> realisationsklasserne hver gang operator new benyttes på en af dem). Når
du
> så i din factory har en "IWhatever *newWhatever( const char
*inSomeArgPSZ )"
> metode, så kalder dens implementation "new RWhatever( inSomeArgPSZ )" og
> kalder dernæst en "initialise" metode på den nye instans, før den
returneres
> (i upcastet interface form, hvilket sker automatisk via den "covariante"
> typekonversion, som det så fint hedder).

Det forstod jeg ikke helt. Siger du at factory skal returnere en pointer til
den øverste parent class og ikke den specifikke class som genereres?
Hvad er fordelen ved det? Og hvorfor lyder "upcastet" fjollet for en
dansker?
Nah spøg til side... kan du forklare hvad du mener fordelen er ved at
returnerer en højere pointer, siden en given factory jo genererer et helt
specifikt objekt - hvis altså ikke der er en param som angiver hvilket
objekt den skal lave?



Mogens Hansen (26-01-2003)
Kommentar
Fra : Mogens Hansen


Dato : 26-01-03 12:57


"Hans Willumsen" <asdg@asdf.awe> wrote

[8<8<8<]
> ... mogens' som ikke
> indeholder andet end citat?

Det var naturligvis en fejl, at jeg fik sendt et indlæg uden indhold.

> Anyway, så er det naturligvis en god ide. Så kan factory, som du jo også
> skriver, håndtere ekstra ting ud over hvad new vil gøre.

Bare for at være sikker på at du er klar over det:
"new" allokerer hukommelse på heapen, og har ikke noget med constructor'en
at gøre.
Constructor forløbet og håndtering af virtuelle metoder kald direkte eller
indirekte fra en constructor har ikke noget at gøre med "new" - der sker det
samme uanset hvor objektet er allokeret.

Venlig hilsen

Mogens Hansen




Mogens Hansen (25-01-2003)
Kommentar
Fra : Mogens Hansen


Dato : 25-01-03 13:14


"Hans Willumsen" <SPAMMh_will@mail.tele.dk> wrote in message
news:b0ttas$2k2n$1@news.cybercity.dk...
> Jeg har en klasse hvor en virtuel metode ikke kaldes som den skal.
> Sagen er i store træk sådan :
>
> class CParent
> {
> protected:
> virtual void SpecifikInitialisering() = 0;
> public:
> CParent(int p1, int p2) { ..initialiser lidt her;
> SpecifikInitialisering();}
> };
>
> class CChild
> {
> protected:
> void SpecifikInitialisering() {gør noget specifikt;}
> public:
> CChild(int p1, int p2) : CParent(p1,p2) {gør intet;}
> };
>
> jeg forventer at når jeg laver en klasse af CChild så vil CParent
> constructoren køre og kalde CChilds udgave af SpecifikInitialisering og
> ikke sin egen. I praksis sker det ikke. Constructoren kalder parents
> specifiikke init, som er abstrakt og det giver en fejl. Sjovt nok ikke en
> compilerfejl men runtime "pure virtuel method called. pres ok to accept
that
> this sucks".
>
> Bruger borland builder 6 hvis det skulle have noget at sige.
>
> ....<5 minuter senere>... hmm.. kan det have noget at gøre med at CChild
> delen ikke er lavet endnu i det CParent constructoren kalder
> SpecifikInitialisering? Det kunne måske tænkes.
> Hvis det er tilfældet, hvordan kommer man så mest elegant ud af det
problem?
> Jeg vil gerne kalde den specifikke initialisering i constructoren... er
jeg
> virkelig nødt til at kalde den explicit i CChilds constructor? og i alle
> andre klasser som skal arve fra parent..?
>
>
>



Mogens Hansen (26-01-2003)
Kommentar
Fra : Mogens Hansen


Dato : 26-01-03 12:57


"Hans Willumsen" <SPAMMh_will@mail.tele.dk> wrote in message
news:b0ttas$2k2n$1@news.cybercity.dk...

[8<8<8<]
> jeg forventer at når jeg laver en klasse af CChild så vil CParent
> constructoren køre og kalde CChilds udgave af SpecifikInitialisering og
> ikke sin egen. I praksis sker det ikke.

Det er i overensstemmelse med C++ Standarden.
Når virtuelle metoder kaldes fra constructor/destructor vil den højest kunne
kalde metoder i den klasse, der er ved at blive oprettet/nedlagt selvom det
endelige objekt er endnu mere specialiseret.

Det er for at undgå at virtuelle metoder kan tilgå datamedlemmer i den mest
afledte klasse, som endnu ikke er oprettet.

Det er overranskende første gang man støder på det, men egentlig meget
forståeligt, når man tænker lidt mere over det.

[8<8<8<]
> ....<5 minuter senere>... hmm.. kan det have noget at gøre med at CChild
> delen ikke er lavet endnu i det CParent constructoren kalder
> SpecifikInitialisering? Det kunne måske tænkes.

Ja.

> Hvis det er tilfældet, hvordan kommer man så mest elegant ud af det
> problem?

Der findes nok ikke en universel løsning på dit spørgsmål.
Du er nok nødt til at gå tilbage og overveje hvad det er for et problem du
_egentlig_ er i gang med at løse.

Der kan være flere scenarier, f.eks.:
1. den afledte klasse skal give noget initialiserings information til
basis-klassen.
2. alle objekter af en given type skal gøre noget bestemt, når de er
oprettet
3. basis-klassen afhænger af statisk information fra den afledte klasse

Ad 1.
I det tilfælde at basis-klassen har brug for noget initialiserings
information for at kunne gennemføre sin constructor, kan den stille sådan et
krav ved enten at tage den information som et constructor argument (runtime
dynamisk) eller et template argument (runtime statisk, compiletime
dynamisk).

<code>
class window
{
public:
window(int x_pos_arg, int y_pos_arg, unsigned width_arg, unsigned
height_arg)
{
}

private:
virtual void paint() const = 0;
};

template <bool horizontal>
class scroll_bar : public window
{
public:
enum { HORIZONTAL_HEIGHT = 10,
VERTICAL_WIDTH = 10
};

scroll_bar(int x_pos_arg, int y_pos_arg, unsigned width_or_height_arg,
int min_scroll_pos_arg, int max_scroll_pos_arg, int scroll_pos_arg)
:
window(x_pos_arg, y_pos_arg,
horizontal ? width_or_height_arg : VERTICAL_WIDTH,
horizontal ? HORIZONTAL_HEIGHT : width_or_height_arg),
min_scroll_pos_(min_scroll_pos_arg),
max_scroll_pos_(max_scroll_pos_arg),
scroll_pos_(scroll_pos_arg)
{}

private:
virtual void paint() const
{
if(horizontal) {
// ...
}
else {
// ...
}
}

private:
int min_scroll_pos_;
int max_scroll_pos_;
int scroll_pos_;
};

typedef scroll_bar<true> horizontal_scroll_bar;
typedef scroll_bar<false> vertical_scroll_bar;


int main()
{
horizontal_scroll_bar sb(10, 10, 20, 0, 100, 0);
}


</code>

Ad 2.
Hvis alle objeter skal gøre noget bestemt men typeafhængigt når de bliver
oprettet, vil det være nærliggende at lægge det i basis-klassens
constructor:

<code>
class window
{
public:
window(int x_pos_arg, int y_pos_arg, unsigned width_arg, unsigned
height_arg)
{
paint();
}

private:
virtual void paint() const = 0;
};

template <bool horizontal>
class scroll_bar : public window
{
public:
enum { HORIZONTAL_HEIGHT = 10,
VERTICAL_WIDTH = 10
};

scroll_bar(int x_pos_arg, int y_pos_arg, unsigned width_or_height_arg,
int min_scroll_pos_arg, int max_scroll_pos_arg, int scroll_pos_arg)
:
window(x_pos_arg, y_pos_arg,
horizontal ? width_or_height_arg : VERTICAL_WIDTH,
horizontal ? HORIZONTAL_HEIGHT : width_or_height_arg),
min_scroll_pos_(min_scroll_pos_arg),
max_scroll_pos_(max_scroll_pos_arg),
scroll_pos_(scroll_pos_arg)
{}

private:
virtual void paint() const
{
if(horizontal) {
// ...
}
else {
// ...
}
}

private:
int min_scroll_pos_;
int max_scroll_pos_;
int scroll_pos_;
};

typedef scroll_bar<true> horizontal_scroll_bar;
typedef scroll_bar<false> vertical_scroll_bar;


int main()
{
horizontal_scroll_bar sb(10, 10, 20,
0, 100, 0);
}

</code>

Problemet er blot - som du har konstateret - at det kan man ikke, fordi
objektet er ikke f.eks. en scroll_bar endnu, og kan derfor ikke tegne sig
rigtigt.

Ud over at bruge en factory, som Igor V. Rafienko og Mads Orbesen Troest har
foreslået, kan man lave en template støtteklasse til basis-klassen der
sikrer at alt er oprettet inden den virtuelle metode kaldes:

<code>
class window
{
public:
window(int x_pos_arg, int y_pos_arg, unsigned width_arg, unsigned
height_arg)
{
}

protected:
virtual void paint() const = 0;
};

template <typename T>
class window_final : public T
{
public:
window_final() :
T()
{
paint();
}
template <typename Arg1T>
window_final(Arg1T arg1) :
T(arg1)
{
paint();
}
// ...
template <typename Arg1T, typename Arg2T, typename Arg3T, typename Arg4T,
typename Arg5T, typename Arg6T>
window_final(Arg1T arg1, Arg2T arg2, Arg3T arg3, Arg4T arg4, Arg5T arg5,
Arg6T arg6) :
T(arg1, arg2, arg3, arg4, arg5, arg6)
{
paint();
}
};

template <bool horizontal>
class scroll_bar_impl : public window
{
public:
enum { HORIZONTAL_HEIGHT = 10,
VERTICAL_WIDTH = 10
};

scroll_bar_impl(int x_pos_arg, int y_pos_arg, unsigned
width_or_height_arg,
int min_scroll_pos_arg, int max_scroll_pos_arg, int scroll_pos_arg)
:
window(x_pos_arg, y_pos_arg,
horizontal ? width_or_height_arg : VERTICAL_WIDTH,
horizontal ? HORIZONTAL_HEIGHT : width_or_height_arg),
min_scroll_pos_(min_scroll_pos_arg),
max_scroll_pos_(max_scroll_pos_arg),
scroll_pos_(scroll_pos_arg)
{}

protected:
virtual void paint() const
{
if(horizontal) {
// ...
}
else {
// ...
}
}

private:
int min_scroll_pos_;
int max_scroll_pos_;
int scroll_pos_;
};

typedef window_final<scroll_bar_impl<true > > horizontal_scroll_bar;
typedef window_final<scroll_bar_impl<false> > vertical_scroll_bar;


int main()
{
horizontal_scroll_bar sb(10, 10, 20,
0, 100, 0);
}

</code>

Ad 3.

Hvis basis-klassen har brug for information fra den afledte klasse, som
_kun_ afhænger af den afledte klasses type (og altså ikke state, som endnu
ikke er initialiseret), kan man statisk føde den information op til
basis-klassen via templates ved hjælp af "curiously recurring template
patterns" (C++ Report, Februar 1995).
Bemærk den _væsentlige_ forskel i strukturen i brugen af templates i
"window_final" i foregående eksempel.

<code>
class window
{
protected:
window(int x_pos_arg, int y_pos_arg, unsigned width_arg, unsigned
height_arg) :
x_pos_(x_pos_arg),
y_pos_(y_pos_arg),
width_(width_arg),
height_(height_arg)
{
}

virtual ~window() {}

protected:
virtual void paint() const = 0;

private:
int x_pos_;
int y_pos_;
unsigned width_;
unsigned height_;
};

template <class T>
class type_window : public window
{
public:
type_window(int x_pos_arg, int y_pos_arg) :
window(x_pos_arg, y_pos_arg, T::default_width(), T::default_height())
{}

type_window(int x_pos_arg, int y_pos_arg, unsigned width_arg, unsigned
height_arg) :
window(x_pos_arg, y_pos_arg, width_arg, height_arg) {}
};

template <bool horizontal>
class scroll_bar : public type_window<scroll_bar<horizontal> >
{
public:
enum { HORIZONTAL_HEIGHT = 10,
VERTICAL_WIDTH = 10
};

scroll_bar(int x_pos_arg, int y_pos_arg,
int min_scroll_pos_arg, int max_scroll_pos_arg, int scroll_pos_arg)
:
type_window<scroll_bar>(x_pos_arg, y_pos_arg),
min_scroll_pos_(min_scroll_pos_arg),
max_scroll_pos_(max_scroll_pos_arg),
scroll_pos_(scroll_pos_arg)
{}

static unsigned default_width()
{ return VERTICAL_WIDTH; }
static unsigned default_height()
{ return HORIZONTAL_HEIGHT; }

protected:
virtual void paint() const
{
if(horizontal) {
// ...
}
else {
// ...
}
}

private:
int min_scroll_pos_;
int max_scroll_pos_;
int scroll_pos_;
};

typedef scroll_bar<true> horizontal_scroll_bar;
typedef scroll_bar<false> vertical_scroll_bar;


int main()
{
horizontal_scroll_bar sb(10, 10,
0, 100, 0);
}

</code>

> Jeg vil gerne kalde den specifikke initialisering i constructoren... er
jeg
> virkelig nødt til at kalde den explicit i CChilds constructor?

constructoren er jo netop lavet til at initialisere objektet, så det lyder
meget fornuftigt.
CParent constructoren har ansvaret for at initialisere CParent delen, og
tilsvarende har CChild constructoren ansvaret for at initialisere CChild
delen.
Hvorfor ikke bare skrive koden direkte i constructoren for hver af de 2
klasser ?

Somme tider har hver klasse flere constructorer, og de deler noget fælles
initialisering som kan være bekvemt at lægge ud i en separat metode - men
det kan man jo bare gøre i hver klasse:

<pseudo C++ code>

class base
{
public:
base(arg_type1)
{ init(); }
base(arg_type2)
{ init(); }

private:
void init()
{ ... }
}:

class derived : public base
{
public:
derived(arg_type1 arg) :
base(arg) { init(); }
derived(arg_type2 arg) :
base(arg) { init(); }

private:
void init()
{ .... }
};

</pseudo C++ code>




Venlig hilsen

Mogens Hansen









Igor V. Rafienko (26-01-2003)
Kommentar
Fra : Igor V. Rafienko


Dato : 26-01-03 16:18

[ Mogens Hansen ]

[ ... ]

> Det er overranskende første gang man støder på det, men egentlig
> meget forståeligt, når man tænker lidt mere over det.


Er det virkelig det? All snakken om at dette var et lurt designvalg,
da man ikke kunne ende opp i en situasjon der man brukte medlemmer som
ikke var initialiserte høres utrolig fjollete ut, med tanke på hvor
mange andre steder C++ standarden tillater nettopp den type oppførsel.

La oss anta at virtuelle funksjoner var virtuelle i constructors (for
diskusjonens skyld):

struct Base
{
int x;

Base() {
   init();
   x = 0;
}

virtual void init() {}
};

struct Derived : Base
{
int y;

Derived() : Base() {}

void init() {
   y = 2*x;
}
};


Gitt noe slikt:

Derived d;

.... så ville d.y ha fått en udefinert verdi. Og så? Det er ikke slik
at en situasjon der en variabel kan bli brukt før den er initialisert
ikke eksisterte fra før av. Ja, det blir udefinert oppførsel av slikt.
Men hvorfor er denne situasjonen så veldig mye verre, siden det er
verdt å forandre på betydningen av "virtual" i to tilfeller (ctor og
dtor).

Løsningen er ikke mer komplisert enn å passe på rekkefølgen ting
initialiseres i. Fx. eksisterer en slik avhengighet allerede mellom
deklarasjonsrekkefølgen i klassen og initialiseringsrekkefølgen i
initializer lists. Jeg synes ikke at denne imaginære "beskyttelsen" er
verdt to unntak i språkdefinisjonen. Dog, det er meget mulig at jeg
går glipp av noe vesentlig.

Jeg testet ideen litt i Python før jeg skrev den forrige artikkelen:

$ cat testbed/virtuality.py
class Base:
def __init__( self ):
   self.init()
   self.x = 0
# end

def init( self ): pass
# end


class Derived(Base):

def __init__( self ):
Base.__init__( self )
# end

def init( self ):
self.y = 2 * self.x
# end
# end

x = Derived()
$ /local/snacks/bin/python testbed/virtuality.py
Traceback (most recent call last):
File "testbed/virtuality.py", line 22, in ?
x = Derived()
File "testbed/virtuality.py", line 14, in __init__
Base.__init__( self )
File "testbed/virtuality.py", line 3, in __init__
self.init()
File "testbed/virtuality.py", line 18, in init
self.y = 2 * self.x
AttributeError: Derived instance has no attribute 'x'
$

Neivel, så den har ikke det. Da gjør man initialiseringen _riktig_ --
_først_ alt som tilhører Base, så spesifikk initialisering (i Base):

class Base:

def __init__( self ):
self.x = 0
self.init()
# end

def init( self ): pass
# end
$ /local/snacks/bin/python testbed/virtuality.py
$

.... og alt virker som det skal (__init__ i Python tilsvarer C++ sin
constructor. Det første argumentet er this-pekeren og må oppgis
eksplisitt. "pass" er syntaktisk sukker for å fortelle at definisjonen
er tom).

Og man kan, naturligvis, om man vil ha det slik, la Base initialisere
kun seg selv, og la alle Derived klassene gjøre et ekplisitt kall på
sin egen initialiseringsfunksjon (det gjelder både Python sin
"framgangsmåte" og C++ sin (fx. slik du foreslo)).


> Ud over at bruge en factory, som Igor V. Rafienko og Mads Orbesen
> Troest har foreslået,


<URL:http://www.parashift.com/c++-faq-lite/strange-inheritance.html>

.... nå som Internettet virker igjen.

[ ... ]


> template <typename T>
> class window_final : public T
> {
[ ... ]
> };


Det er liksom ikke særlig lekkert, da (man må slåss med språket
framfor å bruke det til å løse det egentlige problemet).





ivr
--
<peder> igorr: tcl ja... det er fra de dypeste avgrunnene i helvete det...
<peder> php er bare fra foajeen
            -- pederst på irc

Mogens Hansen (26-01-2003)
Kommentar
Fra : Mogens Hansen


Dato : 26-01-03 22:52


"Igor V. Rafienko" <igorr@ifi.uio.no> wrote in message
news:xjvhebw7xdy.fsf@vestavind.ifi.uio.no...

[8<8<8<]
> Og man kan, naturligvis, om man vil ha det slik, la Base initialisere
> kun seg selv, og la alle Derived klassene gjøre et ekplisitt kall på
> sin egen initialiseringsfunksjon (det gjelder både Python sin
> "framgangsmåte" og C++ sin (fx. slik du foreslo)).

Alle Derived klasserne har en initialiseringsfunktion, som garanteret bliver
kaldt: constructoren.
Det forekommer mig derfor at behovet fra at kalde virtuelle funktioner fra
destructoren skyldes noget andet end initialisering.
Det interessante er hvad det "noget andet" er.

De 2 mest oplagte måder at sende information til en basis-klasses
constructor i C++ er:
* constructor argumenter
* template parameter


[8<8<8<]
> <URL:http://www.parashift.com/c++-faq-lite/strange-inheritance.html>
>

Jeg forstår ikke sammenhængen ...?

[8<8<8<]
> > template <typename T>
> > class window_final : public T
> > {
> [ ... ]
> > };
>
>
> Det er liksom ikke særlig lekkert,

Det er et alternativ til at lave og bruge en factory.

> da (man må slåss med språket
> framfor å bruke det til å løse det egentlige problemet).

Hvad var det _egentlige_ design problem ?
Det er væsenligt for at finde en god løsning.
Hvad det egentlige problem var, er kald af virtuelle metoder fra constructor
ikke løsningen i C++.

Venlig hilsen

Mogens Hansen



Igor V. Rafienko (27-01-2003)
Kommentar
Fra : Igor V. Rafienko


Dato : 27-01-03 11:12

[ Mogens Hansen ]

[ ... ]

> Det forekommer mig derfor at behovet fra at kalde virtuelle
> funktioner fra destructoren skyldes noget andet end initialisering.
> Det interessante er hvad det "noget andet" er.


Uansett _hva_ behovet for et kall på en virtuell funksjon i ctor/dtor
skulle skyldes, så burde programmereren få lov til det. Om det er
_riktig_ framgangsmåte er en helt annen side av saken.

[ ... ]


> > <URL:http://www.parashift.com/c++-faq-lite/strange-inheritance.html>
>
> Jeg forstår ikke sammenhængen ...?


Det var mer til Hans Willumsen.

[ ... ]


> > Det er liksom ikke særlig lekkert,
>
> Det er et alternativ til at lave og bruge en factory.


Det er fortsatt ikke særlig lekkert :)

[ ... ]





ivr
--
<peder> igorr: tcl ja... det er fra de dypeste avgrunnene i helvete det...
<peder> php er bare fra foajeen
            -- pederst på irc

Søg
Reklame
Statistik
Spørgsmål : 177491
Tips : 31966
Nyheder : 719565
Indlæg : 6408458
Brugere : 218886

Månedens bedste
Årets bedste
Sidste års bedste