Het project waar ik mee bezig ben bestaat uit een hoofdprogramma en een static library die daardoor gebruikt wordt. Om de interface van die lib mooi gescheiden te houden van de implementatie binnen de lib heb ik wat verschillende methodes bekeken om voor de gebruikte classes een interface te maken.
Nu heb ik bijvoorbeeld een abstracte class IFolder die als interface dient voor de interne class Folder die de implementatie bevat, en extra methodes die alleen intern aangeroepen mogen worden. Het enige is dat intern steeds Folder wordt gebruikt, en bij de interface alleen parameters als IFolder meegegeven kunnen worden. Als er dus een methode is als addChild(IFolder *pChild), moet pChild eerst gecast worden naar een Folder* voordat deze intern wordt opgeslagen. Op zich is dit geen probleem want de classes worden op 1 na nooit buiten de lib aangemaakt en dus zal een IFolder ook altijd een Folder zijn.
Het wordt pas een probleem bij het implementeren van meerdere interfaces die van elkaar erven. Er is bijvoorbeeld ook een ISubFolder interface (+SubFolder met implementatie), die inherit van IFolder, evenals IRootFolder. De interface IFolder wil ik implementeren met Folder, alle extra methodes die er in subinterfaces bijkomen moeten geimplementeerd worden in de bijbehorende classes, die allemaal weer een subclass zijn van Folder.
In java zou dit prima gaan:
De implementatie van IFolder staat in Folder, die van ISubFolder (-IFolder) in ISubFolder en SubFolder voldoet aan de specs van beide interfaces.
Nu hetzelfde in C++, de interfaces kunnen met een abstracte class gemaakt worden. Door de hierarchie komt IFolder twee keer als superclass voor, maar als deze virtual wordt gemaakt zou het in principe moeten werken:
VC geeft bij deze code een warning (C4250, inheritance via dominance), maar die schijnt puur informatief te zijn (g++ geeft em niet). Dit zou de oplossing moeten zijn maar er kwamen weer problemen zodra de interface gecast moest worden naar zijn implementatie class. Zoals ik al eerder zei zijn er methodes die een interface pointer als parameter krijgen om een bepaald object aan te duiden. Bijvoorbeeld addChild:
Dit gaat natuurlijk niet goed omdat IFolder een *virtual* base class is en dus niet gecast kan worden.
Hoe kan ik dit het beste oplossen? Ik kan ook nog wel kiezen voor een andere interface/implementatie scheiding maar de hierarchie van interfaces en classes zoals hierboven moet wel kunnen.
Nu heb ik bijvoorbeeld een abstracte class IFolder die als interface dient voor de interne class Folder die de implementatie bevat, en extra methodes die alleen intern aangeroepen mogen worden. Het enige is dat intern steeds Folder wordt gebruikt, en bij de interface alleen parameters als IFolder meegegeven kunnen worden. Als er dus een methode is als addChild(IFolder *pChild), moet pChild eerst gecast worden naar een Folder* voordat deze intern wordt opgeslagen. Op zich is dit geen probleem want de classes worden op 1 na nooit buiten de lib aangemaakt en dus zal een IFolder ook altijd een Folder zijn.
Het wordt pas een probleem bij het implementeren van meerdere interfaces die van elkaar erven. Er is bijvoorbeeld ook een ISubFolder interface (+SubFolder met implementatie), die inherit van IFolder, evenals IRootFolder. De interface IFolder wil ik implementeren met Folder, alle extra methodes die er in subinterfaces bijkomen moeten geimplementeerd worden in de bijbehorende classes, die allemaal weer een subclass zijn van Folder.
In java zou dit prima gaan:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| interface IFolder { String getName(); } interface ISubFolder extends IFolder { IFolder getParent(); } class Folder implements IFolder { String getName() { impl; } } class SubFolder extends Folder implements ISubFolder { IFolder getParent() { impl; } } |
De implementatie van IFolder staat in Folder, die van ISubFolder (-IFolder) in ISubFolder en SubFolder voldoet aan de specs van beide interfaces.
Nu hetzelfde in C++, de interfaces kunnen met een abstracte class gemaakt worden. Door de hierarchie komt IFolder twee keer als superclass voor, maar als deze virtual wordt gemaakt zou het in principe moeten werken:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| class IFolder { public: virtual std::string getName() const = 0; }; class ISubFolder : public virtual IFolder { public: virtual IFolder * getParent() const = 0; }; class Folder : public virtual IFolder { public: virtual std::string getName() const { impl; } }; class SubFolder : public Folder, public ISubFolder { public: virtual IFolder * getParent() const { impl; } }; |
VC geeft bij deze code een warning (C4250, inheritance via dominance), maar die schijnt puur informatief te zijn (g++ geeft em niet). Dit zou de oplossing moeten zijn maar er kwamen weer problemen zodra de interface gecast moest worden naar zijn implementatie class. Zoals ik al eerder zei zijn er methodes die een interface pointer als parameter krijgen om een bepaald object aan te duiden. Bijvoorbeeld addChild:
C++:
1
2
3
4
5
6
| (in Folder) virtual void addChild(IFolder *pIFolder) { // Cast naar hele object voor intern gebruik Folder *pf = static_cast<Folder*>(pIFolder); } |
Dit gaat natuurlijk niet goed omdat IFolder een *virtual* base class is en dus niet gecast kan worden.
Hoe kan ik dit het beste oplossen? Ik kan ook nog wel kiezen voor een andere interface/implementatie scheiding maar de hierarchie van interfaces en classes zoals hierboven moet wel kunnen.