#include-Sichtbarkeit auf aufrufenden Header begrenzen [gelöst]



  • Hallo Gemeinschaft,

    ich brauche mal einen Denkanstoss: Ich habe ein Form "Testform" in dessen Header ich ein std::vector im private-Bereich anlege. Dazu wird im Testform-Header #include <vector> benötigt. Ich muss im Hauptform meiner Anwendung den Testform-Header includen um mit Testform arbeiten zu können (zB. dynamisch erstellen), wodurch die Funktionen aus <vector> dann auch im Hauptform verfügbar sind... Solche Vorgehensweisen blähen nun aber mein Projekt unnötig auf, wenn ich das richtig sehe!?
    Wenn ich <vector> in der Quelltextdatei von Testform include und nicht im Header, wird meine Anwendung deutlich kleiner (grob 8000 Zeilen weniger zu compilieren). Allerdings kann ich dann std::vector nur noch als globale Variable in der Quelltextdatei (*.cpp) von Testform anlegen, um aus allen Beispiel-Funktionen Zugriff auf darauf zu haben.
    Kann ich irgendwie festlegen, dass #include <vector> nur in Testform genutzt wird, wenn ich es im Header include?

    Ich hoffe ich habe mich halbwegs verständlich ausgedrückt.

    MfG



  • Du könntest für den privaten Member einen Zeiger auf deinen vector nehmen. Dann reicht im Header eine Vorwärtsdeklaration. Erstellen kannst du den vector ja im Konstruktor (löschen im Destruktor nicht vergessen).





  • Danke für die beiden Vorschläge (die ja eigentlich mehr oder weniger eins sind, richtig?). Das ist ne tolle Sache mit vielen angenehmen Effekten, wenn ich das richtig verstehe... Bezogen auf meine Frage: ich include also <vector> nur in der *.cpp, weil ich std::vector nicht in public des Headers sondern im Konstruktor (Braunstein's Vorschlag)/ in der Struktur-Definition (audacia's Link) deklariere!? Right?

    Sieht ein Zeiger auf std::vector so aus:

    std::vector<unsigned char> *pucVector;
    

    ???

    Ich teste das gleich mal...



  • Am flexibelsten ist es, wenn du dafür pro Klasse eine eigene Struktur anlegst:

    // foo.hpp
    
    #include <memory>
    
    class MyClass
    {
    private:
        struct Implementation;
        std::auto_ptr <Implementation> impl;
    
    public:
        MyClass (void);
        unsigned char bar (int idx);
    };
    
        // foo.cpp
    
    #include <vector>
    #pragma hdrstop
    
    #include "foo.cpp"
    
    struct MyClass::Implementation
    {
        std::vector <unsigned char> pucVector;
    };
    
    MyClass::MyClass (void)
     : impl (new Implementation)
    {}
    
    unsigned char MyClass::bar (int idx)
    { return impl->bar; }
    
    // ...
    


  • Wow... ich hab das Vorgehen aus audacia's Link übernommen und es funktioniert! Danke! 🙂

    Sieht dann bei mir so aus -> Header:

    //---------------------------------------------------------------------------
    #ifndef TestH
    #define TestH
    //---------------------------------------------------------------------------
    //...die üblichen includes...
    //#include <vector>                            ALTE VERSION
    //---------------------------------------------------------------------------
    class TFormTest : public TForm
    {
    __published:
        // ...Form-Komponenten und -Methoden...
    private:
        // ...Variablendeklarationen...
    //  std::vector<unsigned char> Lst;            ALTE VERSION
        struct AList;
        AList *Lst;
        // ...Funktionsdeklarationen...
    public:
        __fastcall TFormTest(TComponent* Owner);
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TFormTest *FormTest;
    //---------------------------------------------------------------------------
    #endif
    

    ->*.cpp:

    //---------------------------------------------------------------------------
    #include <vcl.h>
    #pragma hdrstop
    
    #include <vector>
    
    #include "Test.h"
    #include "Main.h"
    
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TFormTest *FormTest;
    //---------------------------------------------------------------------------
    
    struct TFormTest::AList{ std::vector<unsigned char> Liste; };
    
    __fastcall TFormTest::TFormTest(TComponent* Owner)
    	: 	TForm(Owner),
    		Lst(new AList)
    {}
    //---------------------------------------------------------------------------
    
    void __fastcall TFormTest::MWindowCloseClick(TObject *Sender)
    {
    	delete Lst;
    	this->Close();
    }
    //------------------------------------------------------------------------------
    
    void __fastcall TFormTest::LstBxGetDblClick(TObject *Sender)
    {
        //...
    //  Lst.push_back((unsigned char)Idx);         ALTE VERSION
        WndwLst->Liste.push_back((unsigned char)Idx);
        //...
    }
    //-----------------------------------------------------------------------------
    

    Hat das Ganze jetzt auch irgendwelche Nachteile?

    Jetzt kam grad noch audacia's nächster Post... Schau ich mir gleich an.



  • Kolumbus schrieb:

    Hat das Ganze jetzt auch irgendwelche Nachteile?

    Wenn der Konstruktor von TFormTest eine Exception wirft, wird Lst nicht freigegeben. Deshalb habe ich auto_ptr<> verwendet.

    Außerdem solltest du weder das OnClose-Event, ein eigenes Event (das gar nicht ausgelöst wird, falls dein Formular aus dem Systemmenu geschlossen wird) oder das OnDestroy-Event (das in C++ gefährlich ist) zum Aufräumen benutzen, sondern den Destruktor.



  • audacia schrieb:

    Wenn der Konstruktor von TFormTest eine Exception wirft, wird Lst nicht freigegeben. Deshalb habe ich auto_ptr<> verwendet.

    Ok, auto_ptr<> ist in jedem Fall besser, da man sich damit nie ums Aufräumen kümmern muss...
    Ich habe jetzt also aus *AList Lst; im private-Bereich std::auto_ptr<AList> Lst; gemacht;
    Allerdings zieht das eine Compiler-Warnung nach sich:

    Compiler schrieb:

    [C++Warnung] memory.h(891): Undefined structure 'TFormTest::AList'.

    Was hat das zu bedeuten?

    Edit: Die Beschreibung der Warnung hilft mir nicht wirklich weiter:

    In Ihrer Quelltextdatei wird die angegebene Struktur einige Zeilen vor dem ausgewiesenen Fehler verwendet (eventuell in einem Zeiger auf eine Struktur), ohne vorher definiert worden zu sein.

    Entweder enthält der Strukturname einen Tippfehler oder es fehlt die Deklaration.

    Soll also in diesem Fall heißen: in der memory.h wird die Struktur verwendet ohne vorher definiert worden zu sein??? Weder enthält der Strukturname bei mir einen Tippfehler, noch fehlt die Deklaration. 😕



  • Kolumbus schrieb:

    Allerdings zieht das eine Compiler-Warnung nach sich:

    Compiler schrieb:

    [C++Warnung] memory.h(891): Undefined structure 'TFormTest::AList'.

    Was hat das zu bedeuten?

    Ah, das vergaß ich. Wenn du den Destruktor nicht definierst, wird implizit ein inline-Konstruktor generiert, der auch sämtliche Member destruiert; nun ist aber MyClass::Implementation und ebenso dessen Destruktor im Header noch gar nicht bekannt. MSVC ist da etwas ausführlicher:

    E:\Programs\VS90\VC\INCLUDE\memory(718) : warning C4150: deletion of pointer to
    incomplete type 'MyClass::Implementation'; no destructor called

    Um das Problem zu umgehen, einfach den Destruktor explizit in der .cpp-Datei definieren.

    // foo.hpp
    
    #include <memory>
    
    class MyClass
    {
    private:
        struct Implementation;
        std::auto_ptr <Implementation> impl;
    
    public:
        MyClass (void);
        ~MyClass (void);
        unsigned char bar (int idx);
    };
    
        // foo.cpp
    
    #include <vector>
    #pragma hdrstop
    
    #include "foo.hpp"
    
    struct MyClass::Implementation
    {
        std::vector <unsigned char> pucVector;
    };
    
    MyClass::MyClass (void)
     : impl (new Implementation)
    {}
    
    MyClass::~MyClass (void)
    {}
    
    unsigned char MyClass::bar (int idx)
    { return impl->pucVector.at (idx); }
    
    // ...
    


  • Gut, werd' ich gleich morgen früh ergänzen und mich dann nochmal melden. Danke schonmal soweit 👍



  • Alles klar - compiliert ohne Fehler oder Warnungen! 🙂



  • Hello again,

    ich habe mal noch eine Frage zu der Problematik: Ich habe in einer Methode einfach ganz unbekümmert ein

    std::vector<unsigned char>::iterator XYZ= vecTest.begin();
    

    erstellt. Muss ich mich in diesem Fall selbst um die Freigabe kümmern, oder kann ich den Iterator als eine Art lokale Variable der Methode betrachten?

    MfG

    PS: Ich war mir nicht sicher... wenn für die Frage ein neues Thema im C++ - Forum angebracht wäre, dann sagt Bescheid. 😉



  • Kolumbus schrieb:

    Muss ich mich in diesem Fall selbst um die Freigabe kümmern, oder kann ich den Iterator als eine Art lokale Variable der Methode betrachten?

    Iteratoren sind auch gewöhnliche Objekte, nur eben mit Zeigersemantik.



  • Dankeschön!


Log in to reply