Veidņu cirkulārā atkarība - atsevišķas klases dažādos failos

vispirms es vēlos teikt, ka apzinos, ka šāda veida jautājums ir uzdots jau iepriekš (piemēram, šeit Cirkulāras atkarības atrisināšana starp veidņu klasēm).

Tomēr šis risinājums (Deklarācijas atdalīšana no ieviešanas) darbojas tikai tad, ja abas klases ievieto vienā failā. Manā gadījumā man ir StateManager un State klase, kuras abas ir diezgan lielas, un tām ir garantēta izaugsme. Tādējādi to ievietošana vienā lielā failā man šķiet neapmierinoša.

Šeit ir daži svarīgi koda fragmenti:

// 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;
}

Šeit ir RequestStackPush() metodes ieviešana

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);
    }

Acīmredzot StateManager visu laiku izmanto State klasi. Tas rada tā izsaukšanas metodes utt., tāpēc pārsūtīšanas deklarācija šeit nav risinājums. Lai sniegtu jums piemēru:

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));
    }

Šobrīd abas klases ir vienā lielā failā. Pirmkārt, tiek deklarēta State klases deklarācija ar StateManager, kam seko klases StateManager deklarācija, kam seko iepriekš aprakstītās State::RequestStackPush() metodes ieviešana un visbeidzot visu StateManager veidņu metožu ieviešana.

Kā es varu to sadalīt divos dažādos failos?


person Adrian Albert Koch    schedule 14.12.2017    source avots
comment
Vai esat apsvēris iespēju vienkārši noņemt StateManager atsauci un RequestStackPush metodi? Cik slikti tas varētu ietekmēt pārējo kodu?   -  person Sebastian Redl    schedule 14.12.2017
comment
Slikti, jo tas ir ļoti svarīgi, lai varētu nodot jebkādus datus atvasinātai valstij. Pieprasījuma stāvokļa nosūtīšana ir nepieciešama, lai aizkavētu nosūtītā stāvokļa izveidi. Stāvokļa izveide, atjauninot vai renderējot stāvokļa steku, var radīt kļūdas   -  person Adrian Albert Koch    schedule 14.12.2017
comment
Es domāju, kāpēc cilvēki nevar tieši izmantot StateManager::PushState?   -  person Sebastian Redl    schedule 14.12.2017
comment
@SebastianRedl Tā kā, kā jau teicu, tas var vai faktiski pārtrauks atjaunināšanas cilpu, kurā tiek veikta stāvokļa steka cilpa   -  person Adrian Albert Koch    schedule 14.12.2017


Atbildes (1)


Tomēr šis risinājums (Deklarācijas atdalīšana no ieviešanas) darbojas tikai tad, ja abas klases ievieto vienā failā.

Nē, tas nedarbojas tikai vienā failā. Jūs vienmēr varat izveidot identisku failu, iekļaujot apakšvirsrakstus. Tas vienkārši prasa, lai jūs darītu kaut ko neparastu ar neveidnēm (lai gan tas pats paņēmiens darbojas ar visām iekļautajām funkciju definīcijām): pēc klases definēšanas ir jāiekļauj fails. Galvenes neaprobežojas ar to, ka tās atrodas faila augšdaļā, neskatoties uz tām piešķirto nosaukumu.

Tātad vienā failā:

  • Deklarēt StateManager
  • Definējiet State
  • Iekļaut StateManager definīciju
  • Definējiet dalībnieku funkcijas, kas ir atkarīgas no StateManager definīcijas

Citā failā nekas neparasts:

  • Iekļaut State definīciju
  • Definējiet StateManager un tā dalībnieku funkcijas.

Gala rezultāts ir tāds, ka jebkuras galvenes pievienošana rada tās pašas definīcijas un deklarācijas vajadzīgajā secībā. Tātad šī failu sadalīšana nekādā veidā nepalīdz ierobežot atkārtotas kompilācijas apjomu, ko izraisa vienas galvenes modificēšana.

Tas var būt gaumes jautājums, bet es vienmēr iekļauju definīcijas, kas nepieciešamas iekļautajām funkcijām (tostarp veidņu un veidņu funkciju locekļiem) pēc klases definīcijas. Tādā veidā man nav jāuztraucas, vai tas ir nepieciešams.

person eerorika    schedule 14.12.2017
comment
Pirms StateManager definīcijas viņam ir nepieciešama pilna State definīcija, jo daži funkciju paraksti atsaucas uz iekšējiem tipiem. Bet vispārējā doma ir pareiza. - person Sebastian Redl; 14.12.2017
comment
@SebastianRedl ak, es nepamanīju, ka strīdā būtu atkarība. Labo... Labots. - person eerorika; 14.12.2017
comment
@AdrianKoch State.h nedrīkst ietvert StateManager.h, pirms nav definēts State. Pretējā gadījumā pastāv cirkulāra atkarība. Jums ir jāatbrīvojas no tā, kas ietver. Nav iespējams pateikt, kā to izdarīt, ja nav minimāla reproducējama piemēra. - person eerorika; 14.12.2017
comment
Otrs triks, ko varat izmantot katrā pakārtotā galvenē, ir apgalvot, ka galvenā galvene ir ielādēta, tādējādi jūsu secība ir saglabāta. Standarta galvenes ir pilnas ar piemēriem, piemēram: #if !defined _MATH_H && !defined _COMPLEX_H # error Nekad neizmantojiet ‹bits/mathdef.h› tieši; iekļaujiet “math.h›” vietā #endif - person Gem Taylor; 14.12.2017