Mallin pyöreä riippuvuus - erilliset luokat eri tiedostoissa

Ensinnäkin haluan sanoa, että olen tietoinen siitä, että tällaista kysymystä on esitetty aiemmin (esim. täällä Malliluokkien välisen pyöreän riippuvuuden ratkaiseminen).

Tämä ratkaisu (Deklaroinnin erottaminen toteutuksesta) toimii kuitenkin vain, kun molemmat luokat laitetaan yhteen tiedostoon. Minun tapauksessani minulla on StateManager ja State-luokka, jotka molemmat ovat melko suuria ja kasvavat taatusti. Siksi niiden pitäminen yhdessä suuressa tiedostossa ei mielestäni ole tyydyttävää.

Tässä muutamia tärkeitä koodinpätkiä:

// Forward declare the StateManager --> does not work (incomplete type)
class State
{
public:
    template <class TData>
    void RequestStackPush(ID stateId, std::shared_ptr<TData> data);
private:
    StateManager & stataManager;
}

Tässä menetelmän RequestStackPush() toteutus

template<class TData>
    inline void State::RequestStackPush(ID stateId, std::shared_ptr<TData> data)
    {
        // Uses the state manager's PushState() method - here the issue with the incomplete type arises
        stateManager.PushState<TData>(stateId, data);
    }

On selvää, että StateManager käyttää luokkaa State koko ajan. Se luo se kutsuu menetelmiä jne. joten välitysilmoitus ei ole ratkaisu tässä. Antaakseni sinulle esimerkin:

template<class TData>
    inline void StateManager::PushState(State::ID stateId, std::shared_ptr<TData> data)
    {
        std::unique_ptr<BasePendingChange> pendingChange = std::make_unique<PendingPushDataChange<TData>>(Push, stateId, data);
        pendingChangeQueue.push(std::move(pendingChange));
    }

Tällä hetkellä molemmat luokat ovat yhdessä suuressa tiedostossa. Ensin luokan State ilmoitus, jossa StateManager on ilmoitettu eteenpäin, jota seuraa luokan StateManager ilmoitus, jota seuraa yllä kuvatun State::RequestStackPush()-metodin toteutus ja lopuksi kaikkien StateManager-mallimenetelmien toteutus.

Miten voin erottaa tämän kahdeksi eri tiedostoksi?


person Adrian Albert Koch    schedule 14.12.2017    source lähde
comment
Oletko harkinnut StateManager-viittauksen ja RequestStackPush-menetelmän poistamista? Kuinka huono se olisi muulle koodillesi?   -  person Sebastian Redl    schedule 14.12.2017
comment
Huono, koska se on ratkaisevan tärkeää, jotta kaikki tiedot voidaan välittää johdettuun valtioon. Request State Push -toimintoa tarvitaan työnnetyn tilan luomisen viivyttämiseksi. Tilan luominen tilapinon päivityksen tai renderöinnin aikana voi johtaa virheisiin   -  person Adrian Albert Koch    schedule 14.12.2017
comment
Tarkoitin, miksi ihmiset eivät voi käyttää StateManageria::PushStatea suoraan?   -  person Sebastian Redl    schedule 14.12.2017
comment
@SebastianRedl Koska, kuten sanoin, tämä saattaa tai itse asiassa keskeyttää päivityssilmukan, jossa tilapino silmukataan   -  person Adrian Albert Koch    schedule 14.12.2017


Vastaukset (1)


Tämä ratkaisu (Deklaroinnin erottaminen toteutuksesta) toimii kuitenkin vain, kun molemmat luokat laitetaan yhteen tiedostoon.

Ei, se ei toimi vain yhdessä tiedostossa. Voit aina rakentaa identtisen tiedoston lisäämällä alaotsikot. Se vaatii vain, että teet jotain, mikä olisi epätavallista ei-malleja (vaikka sama tekniikka toimii kaikissa rivin sisäisissä funktiomäärittelyissä): Sinun on sisällytettävä tiedosto luokan määrittämisen jälkeen. Otsikot eivät rajoitu olemaan tiedoston yläosassa niille annetusta nimestä huolimatta.

Eli yhdessä tiedostossa:

  • Ilmoita StateManager
  • Määrittele State
  • Sisällytä määritelmä sanalle StateManager
  • Määritä jäsenfunktiot, jotka riippuvat StateManager:n määritelmästä

Ei mitään epätavallista toisessa tiedostossa:

  • Sisällytä määritelmä sanalle State
  • Määrittele StateManager ja sen jäsenfunktiot.

Lopputuloksena on, että jommankumman otsikon sisällyttäminen tuottaa samat määritelmät ja ilmoitukset vaaditussa järjestyksessä. Joten tämä tiedostojen jakaminen ei millään tavalla auta rajoittamaan yhden otsikon muuttamisesta aiheutuvaa uudelleenkääntämisen määrää.

Se voi olla makuasia, mutta sisällytän aina luokan määrittelyn jälkeen sisäisten funktioiden (mukaan lukien mallien jäsenet ja mallifunktiot) vaatimat määritelmät. Näin minun ei tarvitse huolehtia, onko se tarpeen.

person eerorika    schedule 14.12.2017
comment
Hän tarvitsee State:n täydellisen määritelmän ennen StateManager:n määritelmää, koska jotkut funktioiden allekirjoitukset viittaavat sisäisiin tyyppeihin. Mutta yleinen ajatus on oikea. - person Sebastian Redl; 14.12.2017
comment
@SebastianRedl ah, en huomannut, että riippuvuus oli kiistanalainen. Korjataan... Korjattu. - person eerorika; 14.12.2017
comment
@AdrianKoch State.h ei saa sisältää StateManager.h ennen kuin State on määritetty. Muuten on pyöreä riippuvuus. Sinun on päästävä eroon siitä mukaan lukien. On mahdotonta sanoa miten ilman minimaalinen toistettava esimerkki. - person eerorika; 14.12.2017
comment
Toinen temppu, jota voit käyttää kussakin alaotsikossa, on vakuuttaa, että yläotsikko on ladattu, joten tilauksesi on säilynyt. Vakiootsikot ovat täynnä esimerkkejä, kuten: #if !defined _MATH_H && !defined _COMPLEX_H # error Älä koskaan käytä ‹bits/mathdef.h› suoraan; sisällytä ‹math.h› #endif sijaan - person Gem Taylor; 14.12.2017