Niestety ma ona jedną uciążliwą wadę: kod wykorzystujący boosta strasznie wolno się kompiluje. Przyczyną tego jest fakt, iż boost w dużym stopniu wykorzystuje template-y i to w bardzo zaawansowany sposób. Metaprogramy to dla kompilatora c++ nie lada wyzwanie!
W projektach składających się z kilkudziesięciu plikow źródłowych czas kompilacji całości przekracza kilka minut! Nawet przy kompilacji przyrostowej z użyciem make, po zmianie jednego z headerów w projekcie kompilacja może trwać powyżej minuty!
Jedną z technik przyspieszających kompilację jest unikanie dodawania #include do własnych headerów. Jeżeli tylko można należy wstępnie zadeklarować klasę w headerze, a właściwy #include dodać dopiero do pliku cpp.
Poniżej przykład:
Piszemy klasę Database, w której chcemy skorzystać z klasy XmlDocument. Ściślej: obiekty klasy XmlDocument będą przekazywane jako parametr w metodach klasy Database.
Jak zatem powinniśmy napisać header klasy Database.hpp:
class XmlDocument;
class Database
{
void init( XmlDocument &document );
}
A co jeśli do obiektu XmlDocument chcemy mieć dostęp nie przez referencję, ale przez smart pointer (boost::shared_ptr)?
Żeby zdefiniować smart pointer wcale nie jest potrzebny header klasy. Także tutaj wystarczy jej deklaracja zapowiadająca:
class XmlDocument;
typedef boost::shared_ptr<XmlDocument> XmlDocumentPtr;
class Database
{
void init( XmlDocumentPtr document );
}
Dodatkowa wskazówka: aby uniknąć duplikowania kodu, dla klas z których chcę korzystać poprzez smart pointer, tworzę osobny - dodatowy header KlasaXXXPtr.hpp
Dla przykładu header taki dla klasy XmlDocument będzie wyglądał tak:
#ifndef XMLDOCUMENTPTR_HPP_
#define XMLDOCUMENTPTR_HPP_
#ifndef XMLDOCUMENT_H_
class XmlDocument;
#endif // XMLDOCUMENT_H_
#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<XmlDocument> XmlDocumentPtr;
#endif /* XMLDOCUMENTPTR_HPP_ */
Wszędzie, gdzie chcę zadeklarować smart pointera na XmlDocument po prostu wpisuję #include "XmlDocumentPtr.hpp", natomiast w pliku cpp (np. Database.cpp) muszę jeszcze dodać #include "XmlDocument.hpp".
Zastanawiam się nad deklaracją zapowiadającą do klasy wewnętrznej innej klasy.
OdpowiedzUsuńclass A
{
class a
{
};
};
class B
{
A::a* wskaznik;
};
Deklaracja zapowiadająca
class A::a;
jest niepoprawna. Jak należy to zapisać? A co jeśli struktura klas wewnętrznych będzie jeszcze głębsza?
class A
{
class a1
{
class a2
{
class a3
{
};
};
};
};
class B
{
A::a3* wskaznik;
};
Problem wydaje się być znacznie trudniejszy :)
Pozdrawiam
Łukasz Gotszald
Nie udało mi się zmusić kompilator do zadeklarowania klasy wewnętrznej.
OdpowiedzUsuńPytanie po co nam klasa wewnętrzna?
Wewnętrzna zazwyczaj mówi: internal (nieużywana poza klasą zewnętrzną). Wobec czego potrzeba jej zapowiedzi jest mniejsza.
Czasem wewnętrzna klasa to jakiś helper (klasa pomocnicza), używana poza klasą zewnętrzną.
W takim przypadku przeniósłbym ją poza klasę zewnętrzną. Aby podkreślić związek tych dwóch klas, można wrzucić je do jednego namespace.
Często tak robię: pliki realizujące tę samą funkcjonalność wrzucam w projekcie do jednego folderu, a typy z tych plików wrzucam do wspólnego namespace.
Np.
namespace A_implementation
{
class A;
class AA;
}
To chyba nie jest dobre podejście do problemu: "jeśli nie wiem jak to zrobić to zapytam - po co?". Wtedy ktoś inny mógłby zapytać "po co nam programowanie?".
OdpowiedzUsuńJa znalazłem zastosowanie dla tej techniki, to jest dla mnie ważne i niestety nie można tego obejść bez utraty szybkości algorytmu. Przestrzenie nazw to zupełnie inna sprawa. Ja opisałem problem zapowiadania wskaźnika do klasy wewnętrznej innej klasy a nie ogólny problem tworzenia klas wewnętrznych.
Pozdrawiam
Łukasz Gotszald
W żadnym razie nie chciałem podważyć zasadności używania klas wewnętrznych.
OdpowiedzUsuńChodziło mi raczej o to, iż napotkaliśmy problem: chcemy zadeklarować wskaźnik do klasy wewnętrznej, szukamy, szukamy i... nie znajdujemy rozwiązania. Może nasz kompilator na to nie pozwala?
Pytanie: szukać dalej? A może nie iść "w maliny" i inaczej rozwiązać problem?
W tym przypadku widzę następujące rozwiązania:
a) rezygnujemy z deklaracji zapowiadającej dla wewnętrznej klasy
b) albo przenosimy klasę wewnętrzną na zewnątrz i pakujemy ją do namespace
Pytanie po co nam klasa wewnętrzna miało pomóc w wyborze kompromisu.
Przyznam się, że w większości przypadków gdy deklaruję klasę wewnętrzną w C++, po pewnym czasie i tak ląduje ona na zewnątrz.
I nie dlatego że potrzebuję ją wstępnie zadeklarować, ale dlatego, że jej kod tak się rozrósł iż staje się nieczytelny: plik .hpp zawiera za dużo kodu, a plik .cpp zawiera długie deklaracje w stylu:
void OuterClass::InnerClass::Function()
{
}
Oczywiście każdy ma swój własny styl. Ważne żeby się go trzymać :-)
Pozdrawiam Panie Łukaszu