C++ problems met inheritance

Pagina: 1
Acties:

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 28-08 12:00

Janoz

Moderator Devschuur®

!litemod

Topicstarter
Zoals enkelen van jullie wel weten ben ik al een tijdje met een C++ (m'n eerste) project bezig en ben daarbij al tegen een aantal problemen aangelopen. Al eerder had ik een topic geopend over het initialiseren van variabelen en heb na aanleiding van dat topic besloten om een protected init methode te maken.

Nu het project wat vordert blijkt het handiger te zijn om een class op te splitsen. Deze class zit namelijk vol met een berg functionaliteit die in veel gevallen helemaal niet nodig is. Daarnaast moet er een nieuwe class komen die een kleine uitbreiding heeft, maar voor de rest op het absolute minimum van die class gebaseerd kan worden. Om een lang verhaal kort te maken, de class wordt omgezet in een lite versie, een gewone versie en een extended versie en er word een andere class gemaakt die de lite versie derived.

Het probleem

Omdat er in die initialisatie fase nogal veel gebeurt, en elke class een berg constructors heeft heb ik in eerste instantie gekozen voor een init functie. Deze init functie wordt ook aangeroepen op het moment dat ik de class 'vernieuw'. Nu ik de boel opsplits loop ik tegen het probleem aan dat de init functie meerdere keren uitgevoerd kan worden in plaats van 1. Als voorbeeld heb ik ff dit programmatje:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Base{
public:
  Base();
  renew();
protected;
  void init();
private:
  int a;
};

class Derived : public Base {
public:
  Derived();
  renew();
protected();
  void init();
private:
  int b;
};


Base::Base(){
  init();
}
void Base::renew(){
  init();
}
void Base::init() {
  a = 0;
}
Derived::Derived() {
  init();
}
void Derived::renew() {
  // Base::inite(); ??
  init()
}
void Derived::init() {
  b = 0;
  // Base::init(); ??
}


Als ik nu de constructor van Derived aanroep wordt de constructor van Base aangeroepen en met init a op nul gezet. vervolgens wordt init in Derived aangeroepen en b op 0 gezet. So far so good.

Het probleem is nu waneer ik renew() aanroep op Derived. Deze zal alleen b op 0 zetten en a niet. Ik kan natuurlijk in renew() ook Base::init() aanroepen, maar bij de extended versie wordt het dan wel een beetje druk (eerst lite::init() vervolgens gewoon::init() en tot slot de eigen init()).

Ik zou het op kunnen lossen door in init de herarchie te doorlopen, dus bij een aanroep van init telkens ook de init van 'super' aan te roepen, maar dan krijg je het probleem bij de constructoren dat de init van de Base class meerdere keren aangeroepen wordt.

In principe zou dit probleem natuurlijk zo opgelost zijn waneer er zoiets als in java mogelijk was geweest. Hierin kun je bij je bij 'globale' class variabele declaraties gewoon een initiele waarde opgeven, maar uit m'n vorige topic was ik al tot de conclusie gekomen dat dat niet mogelijk was.

Wat wil ik nou?

Aangezien ik nog maar net werkelijk met C++ bezig ben, ben ik nog niet zo heel erg bekend met de conventies en standaarden voor c++-code. Wat is nu eigenlijk de standaard/beste manier? Het liefst schrijf ik zo min mogelijk dubbele code (kan alleen maar voor fouten zorgen, ene deel wel aanpassen en het andere deel vergeten) en maak ik het zo generiek mogelijk (niet dat je je in een derived class van de derived class nog druk moet maken wat er in de base class gebeurt).

[ Voor 0% gewijzigd door Janoz op 17-09-2002 23:19 . Reden: Wat spelfouten :) ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
In de reeks zeg het met code :P

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class Base{
        public:
                Base();
                void renew();
        private:
                void init();
                void setA( int a );
                int getA( );

                int a;
};

class Derived : public Base {
        public:
                Derived();
                void renew();
        private:
                void init();
                void setB( int b );
                int getB( );

                int b;
};


Base::Base(){
        init();
}

void Base::renew(){
        init();
}
void Base::init() {
        setA( 0 );
}

void Base::setA( int a ) {
        //fatte controles op A hier
        this.a = a;
}

int Base::getA( ) {
        return a;
}

Derived::Derived() {
        init();
}
void Derived::renew() {
        //Renew our Parentobject
        //
        Base::renew( );

        // Renew this object
        //
        init()
}

void Derived::init() {
        //Eigenlijk hier een mooie constante
        //
        setB( 0 );

        //Het initten van de base lijkt me niet nodig hier
        //aangezien init een private memberfunction is en toch
        //alleen dit object zou moeten vernieuwen.
}

void Derived::setB( int b ) {
        //fatte checks op b hier
        //
        this.b = b;
}

int Derived::getB( ) {
        return b;
}


Ik persoonlijk (java achtergrond) denk dat dit de beste oplossing is. Wat heb ik allemaal gedaan (en niet getest :P )

Ten eerste heb ik geweigerd om protected methods aan te maken. Hiermee geef je dus ware Derrived toegang tot de init( ) methode van Base. Wil je dat? Nou nee, eigenlijk wil je alleen dat de constructor init( ) kan uitvoeren en het object kan initten.
Echter jij wilt in dit geval dus dat de Derrivedclass de baseclass kan verversen: maak daar dan ook een method voor -> renew( ), toevallig gebruikt deze de init( ) methode, tja maar hij kan toch ook nog veel meer doen?

Verder geef je aan dat je niet veel code op verschillende plaatsen hebt. Waarom maak je dan geen getters/setters? Dan heb je alle checks voor bijv de int a in de Baseclass op 1 plek staan (in de set method) en niet in je init() en in een andere methode bijv.

Verder lijkt het me niet raar om in de renew( ) van Derrived een verwijzing naar de renew( ) van Base te zetten. Je typt geen code dubbel omdat als je ergens anders Base wilt vernieuwen, je gewoon renew( ) van Base kan aanroepen. Wil je het hele object weer vernieuwen dan volstaat renew( ) van Derrived. Renew( ) van Base moet zelf maar zorgen dat hij z'n Baseclasses ook vernieuwd.

Verder zou ik geen Base::renew( ) willen aanroepen in je init( ) van Derrived. Dit omdat init( ) alleen het huidige object moet initten en niet het Base object verversen. Deze wordt toch wel aangeroepen omdat hij maar op 2 manieren vernieuwd kan worden : via de constructor en via renew( ) van Derrived

Uhm schiet me maar af, als ik onzin praat!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 28-08 12:00

Janoz

Moderator Devschuur®

!litemod

Topicstarter
Glimi schreef op 17 september 2002 @ 23:31:
Ten eerste heb ik geweigerd om protected methods aan te maken. Hiermee geef je dus ware Derrived toegang tot de init( ) methode van Base. Wil je dat? Nou nee, eigenlijk wil je alleen dat de constructor init( ) kan uitvoeren en het object kan initten.
Echter jij wilt in dit geval dus dat de Derrivedclass de baseclass kan verversen: maak daar dan ook een method voor -> renew( ), toevallig gebruikt deze de init( ) methode, tja maar hij kan toch ook nog veel meer doen?
protected was nodig om vanuit derived init in base aan te roepen. In het werkelijke programma zijn de methode's die hier renew voorstellen ook protected aangezien ze niet zomaar aangeroepen mogen worden. Ook met jou oplossing zou renew protected moeten worden dan :)
Verder geef je aan dat je niet veel code op verschillende plaatsen hebt. Waarom maak je dan geen getters/setters? Dan heb je alle checks voor bijv de int a in de Baseclass op 1 plek staan (in de set method) en niet in je init() en in een andere methode bijv.
Uiteraard heb ik getters en setters, maar die heb ik voor de duidelijkheid maar ff weggelaten (waren niet van toepassing op dit probleem).
Verder lijkt het me niet raar om in de renew( ) van Derrived een verwijzing naar de renew( ) van Base te zetten. Je typt geen code dubbel omdat als je ergens anders Base wilt vernieuwen, je gewoon renew( ) van Base kan aanroepen. Wil je het hele object weer vernieuwen dan volstaat renew( ) van Derrived. Renew( ) van Base moet zelf maar zorgen dat hij z'n Baseclasses ook vernieuwd.

Verder zou ik geen Base::renew( ) willen aanroepen in je init( ) van Derrived. Dit omdat init( ) alleen het huidige object moet initten en niet het Base object verversen. Deze wordt toch wel aangeroepen omdat hij maar op 2 manieren vernieuwd kan worden : via de constructor en via renew( ) van Derrived
hmm .. idd, stom dat ik daar niet aan gedacht heb :).. Ik zal eens ff kijken of dit toepasbaar is op mijn project (wat uiteraard iets uitgebreider is dan dit stukje voorbeeld code :) )

[ Voor 0% gewijzigd door Janoz op 18-09-2002 00:20 . Reden: Ook maar ff aangepast aangezien Glimi een complete motivatie toegevoegd heeft :) ]

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Janoz schreef op 17 september 2002 @ 23:44:
protected was nodig om vanuit derived init in base aan te roepen. In het werkelijke programma zijn de methode's die hier renew voorstellen ook protected aangezien ze niet zomaar aangeroepen mogen worden. Ook met jou oplossing zou renew protected moeten worden dan :)
Waarom mogen andere classes niet gewoon de boel leeggooien? Ze kunnen toch ook de instantie van de class wegflikkeren? Ik bedoel als ze geen const reference ernaar toe hebben, waarom niet dan?
Uiteraard heb ik getters en setters, maar die heb ik voor de duidelijkheid maar ff weggelaten (waren niet van toepassing op dit probleem).
first bullet hits! ;)
hmm .. idd, stom dat ik daar niet aan gedacht heb :).. Ik zal eens ff kijken of dit toepasbaar is op mijn project (wat uiteraard iets uitgebreider is dan dit stukje voorbeeld code :) )
Toch fijn dat ik je code dan nu niet voor je geschreven hebt, een verademing ipv de php topics :) keep me/us informed en succes! :>

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 28-08 12:00

Janoz

Moderator Devschuur®

!litemod

Topicstarter
Glimi schreef op 18 september 2002 @ 00:28:
Waarom mogen andere classes niet gewoon de boel leeggooien? Ze kunnen toch ook de instantie van de class wegflikkeren? Ik bedoel als ze geen const reference ernaar toe hebben, waarom niet dan?
Het ligt wat ingewikkelder :).. en nee, andere classes mogen de boel niet zomaar weggooien. Deze worden in het werkelijke programma aangeroepen waneer er bv nieuwe data wordt ingeladen, of de lengte van bepaalde dingen wordt veranderd.
Toch fijn dat ik je code dan nu niet voor je geschreven hebt, een verademing ipv de php topics :) keep me/us informed en succes! :>

Hoofdstuk 1 is alleen nog maar af, maar zodra er meer is, is dit hier te lezen. (of hier waneer de wiskundige defenities de boel verneuken)

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'