[C++]classes met referenties naar elkaar

Pagina: 1
Acties:

  • CyBoB
  • Registratie: Januari 2001
  • Laatst online: 24-12-2025

CyBoB

.::BURB::.

Topicstarter
ik wist niet zo goed wat voor titel ik het moest geven, maar ik zit met hetvolgende probleem.

ik wil in mijn applicatie een centraal punt hebben waar bepaalde objecten zichzelf kunnen "registeren". ik heb/wil dit doormiddel van een singleton doen. nu ben ik oorspronkelijk een java progger, waarmee dit me prima lukt alleen stuit ik op een probleem in C++. omdat het wat lastig uitleggen is heb ik hier even een klein voorbeeldje van wat ik wil.

hier de code in C++ (wat dus niet werkt!!, bestaande uit Foo, FooManager, FooApp (.h & .cpp files))

Foo.h
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef _FOO_H_
#define _FOO_H_

#include <iostream>
#include "FooManager.h"

class Foo{
    private:
        FooManager * fm;
    public:
        Foo();

        void DoFoo();
};

#endif


Foo.cpp
C++:
1
2
3
4
5
6
7
8
9
10
11
12
#include "Foo.h"

Foo::Foo(){
    fm = FooManager::GetInstance();
    fm->RegisterFoo(this);

    std::cout << "Foo Created" << std::endl;
}

void Foo::DoFoo(){
    std::cout << "DoFoo" << std::endl;
}


FooManager.h (volgens het singleton pattern)
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef _FOOMANAGER_H_
#define _FOOMANAGER_H_

#include <iostream>
#include "Foo.h"

class FooManager{
    private:
        static FooManager * _instance;  //singleton instance
        Foo * m_foos[10];   //store 10 foos
        int m_nFooCount;
    protected:
        FooManager();
    public:
        static FooManager * GetInstance(); //retrieve instance
        void RegisterFoo(Foo * f);
};
#endif


FooManager.cpp
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "FooManager.h"
#include "Foo.h"

FooManager * FooManager::_instance = 0;

FooManager * FooManager::GetInstance(){
    if(_instance == 0)
        _instance = new FooManager();
    return _instance;
}

FooManager::FooManager(){
    m_nFooCount = 0;
    std::cout << "FooManager Created" << std::endl;
}

void FooManager::RegisterFoo(Foo * f){
    m_foos[m_nFooCount++] = f; //dit geeft nog geheugen foutmeldingen maar is op dit moment niet belangrijk
    std::cout << "Foo Registered" << std::endl;
}


FooApp.cpp (main enzo..)
C++:
1
2
3
4
5
6
7
#include "FooManager.h"
#include "Foo.h"

int main(){
    Foo f;  
    f.DoFoo();
}


Deze code geeft dus problemen. Voor zover ik heb kunnen concluderen komt dit door de dubbele referentie van Foo naar FooManager en FooManager naar Foo. De compiler snapt er namelijk helemaal niks meer van (wat ik opzig wel begrijpelijk vind), maar als ik het equivalent in java prog dan werkt het allemaal prima.

de compiler komt met de meldingen: "missing storage-class or type specifier"
verder nog meer errors, maar die zijn naar mijn mening allemaal het resultaat van deze foutmelding

hier volgt de vergelijkbare java code: Foo.java, FooManager.java, FooApp.java

Foo.java
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import FooManager;

public class Foo{
    private FooManager fm;

    public Foo(){
        fm = FooManager.getInstance();
        fm.registerFoo(this);
        
        System.out.println("Foo Created");
    }
    public void doFoo(){
        System.out.println("DoFoo");
    }
}


FooManager.java
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Foo;

public class FooManager{
    private static FooManager _instance = null;
    private Foo foos[];
    private int fooCount;

    public static getInstance(){
        if(_instance == null){
            _instance = new FooManager();
        }
        return _instance;
    }
    
    public FooManager(){
        foos = new Foo[10];
        fooCount = 0;
        System.out.println("FooManager Created");
    }
    public void registerFoo(Foo foo){
        foos[fooCount++] = foo;
        System.out.println("Foo Registered");
    }
}


FooApp.java
Java:
1
2
3
4
5
6
7
8
9
import Foo;

public class FooApp{
    public static void main(String[] args){
        Foo foo = new Foo();

        foo.doFoo();
    }
}


de bovenstaande code werkt dus prima. ik krijg het in c++ alleen aan de praat als ik bijvoorbeeld de "Foo" class subclass van een "Fum" class en dan in "FooManager" niet een "Foo" class te registeren maar een "Fum" class... dit is natuurlijk totaal niet mooi omdat ik dan steeds in "FooManager" al die "Fum" instanties moet gaan casten naar "Foo" instanties (met de alle foutgevoeligheid van dien).

ik hoop dat mijn verhaal een beetje duidelijk is en dat iemand mij kan vertellen of het uberhaupt mogelijk is op de manier zoals ik dat wil. zo ja wat doe ik verkeerd of moet er verandert worden. andere suggesties zijn altijd welkom.

[ Voor 6% gewijzigd door CyBoB op 25-03-2004 16:36 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 26-05 23:14
Het probleem is dat je eerst in "Foo.h" gebruik wil maken van FooManager en dus de bijbehorende header file importeert en in "FooManager.h" gebruik wil maken van Foo en dus precies het omgekeerde doet. Zo'n cyclische referentie werkt natuurlijk niet; je header guards (die IFNDEF/DEF/ENDIF-dingen) voorkomen gelukkig dat beide files oneindig vaak geinclude worden, maar dat neemt niet weg dat een van de headers het eerste wordt gecompileerd en dan ontbreekt daar de declaratie uit de tweede.

In dit geval is het probleem gelukkig eenvoudig op te lossen. In plaats van de volledige klassedefinitie (in de header files) te includen kun je volstaan met een klassedeclaratie (ook wel forward declaration genoemd). Vervang in "Foo.h" bijvoorbeeld #include "FooManager.h" door class FooManager; (en eventueel hetzelfde maar dan met Foo in "FooManager.h"). Je vertelt de compiler dan dat er een klasse genaamd FooManager bestaat en dat is precies wat 'ie nodig heeft om je header file te compileren. Uiteraard moet je de volledige definitie nog wel ergens anders opnemen als je er daadwerkelijk iets mee wil doen (zoals er functies op aanroepen zoals je in "Foo.cpp" doet) maar dat kun je uitstellen tot in je source file.

In het algemeen doe je er verstandig aan niet meer files te includen dan strict noodzakelijk, zeker in header files. Als je dus in een header file alleen een klassedeclaratie nodig hebt, kun je daarmee volstaan en is het niet nodig de hele definitie uit een andere header file halen. Dit soort veelvoorkomende problemen zijn op die manier meestal wel op te lossen.

Respect voor je uitgebreide topic start, trouwens! :Y)

[ Voor 42% gewijzigd door Soultaker op 25-03-2004 16:46 ]


  • CyBoB
  • Registratie: Januari 2001
  • Laatst online: 24-12-2025

CyBoB

.::BURB::.

Topicstarter
oke volkomen duidelijk, ik ga het meteen in me echte code implementeren :)

bedankt voor de snelle reactie

ik heb alleen nog 1 ding en dat is het volgende. nu ik dus geen #include heb meer van "Foo.h" in "FooManager.h/cpp" en alleen een class definitie zou je denk dat als ik in de "Foo.h" bijvoorbeeld een "#define FOO_CONSTANT 10" opneem dat ik deze dan niet meer kan benaderen.

dit is echter wel het geval. nu vermoed ik dat dit komt omdat de "Foo.h" uiteindelijk toch wel ergens ge'#include word (welis waar ergens anders dan in FooManager.h(.cpp)) en alsnog beschikbaar is.

is deze aannamen correct?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 14:50

.oisyn

Moderator Devschuur®

Demotivational Speaker

Nee, als je in FooManager.h niets meer include, en in Foo.cpp alleen iostream, dan zou je er niet bij moeten kunnen komen. Dus ik vermoed dat wat je zegt niet waar is: of in FooManager.h of in FooManager.cpp staat een include-regel die Foo.h include, of juist een derde header die op zijn beurt weer (al dan niet direct) Foo.h include.

Overigens zei je classdefinitie terwijl je classdeclaratie bedoelde (en het verschil tussen die 2 is nou net de essentie van je probleem) ;)

[ Voor 20% gewijzigd door .oisyn op 25-03-2004 17:08 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • CyBoB
  • Registratie: Januari 2001
  • Laatst online: 24-12-2025

CyBoB

.::BURB::.

Topicstarter
ah idd je hebt gelijkt, ik had nu en een class declaratie (zat net even te twijfelen wat nu wat was :P ) en een #include staan (class declaratie in de header- en de #include in de .cpp file). na het weghalen van de include werkt het niet meer, maar dit wil ik wel dus hou ik die include er toch maar lekker in :)

[ Voor 15% gewijzigd door CyBoB op 25-03-2004 17:25 ]