Vzájemné includování hlaviček dvou tříd (circular dependencies) – C / C++ – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Vzájemné includování hlaviček dvou tříd (circular dependencies) – C / C++ – Fórum – Programujte.comVzájemné includování hlaviček dvou tříd (circular dependencies) – C / C++ – Fórum – Programujte.com

 

Lukáš
~ Anonymní uživatel
301 příspěvků
11. 6. 2015   #1
-
0
-

Zdravím,

jsem začátečník v C/C++ a mám velký problém s pochopením toho, jak se mají správně navzájem includovat spolu související třídy.

Resp. možná s tím, jak používat globální hlavičkový soubor.

Jde o to, že mám např. třídy Class1 a Class2, kdy jedna využívá druhou a druhá tu první. V jedné z nich mám ale třeba includovaný ještě nějaký globální hlavičkový soubor (který includuji skoro všude).

Teď si ani nejsem jestli jestli je problém v tom, že chci includovat Class1 k Class2 a naopak a nebo až v tom, že dochází k opakovanému načítání toho globálního .h souboru.

Narážím na to pořád dokola a přestože to vždycky nějak o*ebu, tak mě vadí, že to je základní věc a já se s tím musím pořád prát. Knížek už jsem přečetl několik a různých tutoriálů na netu taky, ale tam se řeší jednodušší případy.

K tomuto problému jsem četl i jeden velmi kvalitní článek (myslím že na http://www.cplusplus.com/forum/) kde byla hromada problémů vysvětlena, ale myslím že ani tam nebyly všechny případy na které narážím. Autor se tam zmiňoval o dopředné deklaraci (Forward declaration), ale tu se mi ještě nikdy nepovedlo použít. Vždycky mě překladač nahlásí, že je to neúplná deklarace a chce hlavičkový soubor.

Nemáte někdo tip, kde bych našel detailní vysvětlení a ideálně ukázky řešení takových případů? Díky.

Nahlásit jako SPAM
IP: 212.4.134.–
Quimby
~ Anonymní uživatel
10 příspěvků
11. 6. 2015   #2
-
0
-

V každém .h souboru přidej na první řádek

#pragma once

Forward Declaration znamená, že místo tohohle:

#include "B.h"

class A
{
...
	B otherclassdata;
...
};

uděláš:

class B;


class A
{
...
	B otherclassdata;
...
};
Nahlásit jako SPAM
IP: 81.200.55.–
Lukáš
~ Anonymní uživatel
301 příspěvků
11. 6. 2015   #3
-
0
-

Díky, ale...

#pragma once

Nepomohlo - žádná změna. Pokud vím, tak toto používá IDE od Microsoftu, že? Já mám Qt IDE a tam je to řešeno přes:

#ifndef CLASS_H
#define CLASS_H
#endif // CLASS_H

Jinak co je dopředná deklarace vím - jen, jak jsem psal v prvním postu, jsem ji nikdy nemohl použít, protože při jejím použití chce překladač i hlavičkový soubor.

Nahlásit jako SPAM
IP: 212.4.134.–
Ovrscout
~ Anonymní uživatel
113 příspěvků
12. 6. 2015   #4
-
0
-

#3 Lukáš
Překladač neví kolik má pro tu proměnnou "B" vyhradit  místa pokud není plně definována.
Můžeš to ale obejít tak že použiješ ukazatel(ten má celkem jasnou velikost):

class B;


class A
{
...
	B *otherclassdata;
...
};

Samozřejmně když budeš chtít s "B" pracovat, tak musíš mít v .c souboru includnuté oba hlavičkové soubory.

Ukazatel je třeba správně inicializovat, několik základních možností:

  • v konstruktoru se objekt vytvoří (pomocí new) a adresa se přiřadí do B
  • Do konstruktoru předáš odkaz na existující objekt a jen jej přiřadíš do proměnné B
  • Nebo "odkazem" mezi objekty vytvoříš později. Pomocí funkce Registruj nebo Propoj cizí objekt
    (v konstruktoru ale doporučuju B vynulovat a tam kde se používá kontrolovat zda je správně nastavena (tj. není nula)

Je třeba táké dát pozor na to jak se budou objekty uvolňovat, pokud je v konstruktoru vytvoříš tak v destruktoru uvolníš, to je jasné/snadné. Ale Pokud máš vazbu odkazem, je vhodné ji správně přerušit dokud oba ojekty existují, a nebo použít něco jako chytré ukazatele (smart pointers).

Případně se na to podívat z jiného úhlu, nějak nepřekopat abys to nemněl takhle křížem.
Např tak že třídy se nebudou vnořovat ale budou spolu komunikovat přes nějaké rozhraní(eventy,notifikace,roura,fronta,..)  nebo že společný kód přesuneš do jiné třídy kterou pak budou obě vkládat . Možná se i zamyslet zda by nešla použít dědičnost (tj obecný společný předek a jeho specializace A,B)
Tohle se obecně nedá dost dobře říci, záleží na konkrétní situaci jak si s objekty pohrát

Nahlásit jako SPAM
IP: 193.165.79.–
Lukáš
~ Anonymní uživatel
301 příspěvků
13. 6. 2015   #5
-
0
-

#4 Ovrscout
Díky za vyčerpávající odpověď. Práce s pointery a referencemi atp. je mi jasná. Problém má jen tady v tom includování. Popravdě si myslím, že hlavní problém bude spíš v logickém návrhu - zkušenější C-čkař by to asi navrhl lépe. Já sice s OOP zkušenosti mám, ale jen z PHP, kde je to úplně jiné a includování nemusím řešit.

Problém už jsem více-méně vyřešil a to dvojím způsobem. Za prvé jsem špatně chápal dopřednou deklaraci. Já myslel, že stačí dát jen to class Class1; a nemusím tu třídu includovat. Ale když jsem tam to #include nechal a navíc přidal tu deklaraci, tak to funguje. Bohužel mi moc není jasné proč (vždyť tam to problémové #include zůstalo), ale snad mi to časem dojde.
Druhá změna byla v tom návrhu - měl jsem tři třídy na úrovni "vedle sebe", ale nakonec jsem to udělal tak, že jedna hlavní si vytváří instance těch dvou dalších.

Jak říkám, problém asi nejčastěji bude ve špatném návrhu těch tříd - častěji se mi stává, že něco vymyslím a pak to poměrně razantně předělám.

Ještě jednou díky :)

Nahlásit jako SPAM
IP: 212.4.134.–
ondrej39+1
Věrný člen
13. 6. 2015   #6
-
0
-

#5 Lukáš
1) Header guardy ve všech headerech.

2) Ve všech .cpp includuj úplně všechno, co v daném .cpp používáš. Dáváš tím jasně najevo, že z této knihovny si budeš brát nějaké funkce, nebo používat daný objekt. Pokud již daný header naincludovaný bude, díky guardu se ti nenaincluduje znovu.

3) Circular dependency nemusí nutně znamenat úplně špatný návrh aplikace, v některých případech to tak chceš mít, ale v některých ne. Kvůli tomu bohužel ale namísto dvou objektů vlastně vytváříš jeden a pokud něco změníš v jedné třídě, ovlivní to i třídu druhou, což povede k nutnému přeložení.

Dopřednou deklaraci děláš proto, abys dal překladači najevo, že bude existovat nějaká třída. Třída ovšem musí být definována. A ta definice se převede z includovaného headeru (a't je to "A", nebo "B"), samotná deklarace nestačí...

Nahlásit jako SPAM
IP: 46.39.172.–
Inject all the dependencies!
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 21 hostů

Moderátoři diskuze

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý