Designfrage: Vererbung



  • Ich habe, stark vereinfacht, folgendes Szenario:

    Die public Methoden meiner Klasse sollen auf weitere Methoden zugreifen, die vom Klienten veraendert werden koennen bei Bedarf. Diese veraenderbaren Methoden benoetigen Zugriff auf einige Member der Klasse. Ich habe das momentan mit dem template method pattern geloest:

    class P {
    public:
      void foo() {bar1(); bar2(); bar3();}
      virtual ~P() {}
    protected:
      virtual void bar1() {i1 = 3;}
      virtual void bar2() {i2 = 4;}
      virtual void bar3() {i3 = 5;}
    
      int i1, i2, i3;
    };
    

    Der Klient kann so von der Klasse ableiten, die bar-Methoden bei Bedarf ueberschreiben und alles ist schoen - bis auf den uneingeschraenkten Zugriff aller ableitenden Klassen auf i1, i2, i3.

    Gibt es eine Moeglichkeit, den Zugriff auf die bar-Methoden der ableitenden Klassen zu beschraenken?



  • private:
        int i1, i2, i3;
    


  • Dann tuts folgendes aber nicht mehr:

    class T : public P {
    protected:
      virtual void bar1() {i1 = 15;} //FEHLER: i1 ist private member von P udn in T nicht zugaenglich
    };
    

    obiges sollte aber moeglich sein. Andererseits will ich folgendes verhindern:

    class U: public P {
    public:
      void meep() {i1 = 24;}  //ist erlaubt, da i1 protected member von P
    };
    

    Wie gesagt, auf i1, i2, i3 soll in abgeleiteten KLassen zugegriffen werdne koennen, allerdings NUR innerhalb der bar() methoden



  • Nun ja...es laesst sich da immer etwas zusammencode 😃 Ueber den Sinn darf sich streiten lassen.

    class P
    { 
    public: 
      void foo()
      {
        access = true;
        bar1();
        bar2();
        bar3();
        access = false;
      }
    
      virtual ~P() {}
    
    protected:
      void SetI1(int i)
      {
        if (access)
          i1 = i;
      }
    
      void SetI2(int i)
      {
        if (access)
          i2 = i;
      }
    
      void SetI3(int i)
      {
        if (access)
          i3 = i;
      }
    
      virtual void bar1()
      {
        SetI1(3);
      }
    
      virtual void bar2()
      {
        SetI2(4);
      }
    
      virtual void bar3()
      {
        SetI3(5);
      }
    
    private:
      bool access;
      int i1, i2, i3; 
    };
    

    Ich denke mal, das ist (so in etwa) was du wolltest.

    Gruß,
    DeSoVoDaMu



  • Naja, auch net so ganz... die SetI's koenne ja immer aufgerufen werden, nur ob sie Effekt haben ist dann ne andere frage. Da Lass ich demjenigen ders dann ableitet lieber den Zugriff als ihm was vorzusetzen was zwar funktioniert, aber nicht so wie ers vielleicht erwartet...



  • Hi Pumuckl,

    die Regel ist doch ganz einfach: Felder immer privat. Dann definierst Du Dir in der Basisklasse protected-Setter-Methoden für die Felder, die Dir den Zugriff regulieren.



  • Hi Konrad,
    klar, die Setter Methoden sollte ich auf jeden Fall implementieren. Das ändert allerdings an der eigentlichen Problematik nichts: Ich kann dann in alle abgeleiteten Klassen auch außerhalb der bar()-Methoden auf diese Setter zugreifen. Ich war schon am Überlegen ob es ne Möglichkeit gibt, das über friends und evtl. Funktoren zu regeln, komme da aber auf keinen grünen Zweig.



  • So fein granular kannst du die Zugriffskontrolle in C++ nicht einstellen - friend wäre eventuell eine Möglichkeit, aber da mußt du die bar()-Methoden ALLER potentiellen abgeleiteten Klassen auflisten.

    Frage: Dürfen die bar()-Methoden der abgeleiteten Klassen deine Member beliebig ändern? Oder nur innerhalb der Möglichkeiten, die du vorgesehen hast?
    (und aus persönlichem Interesse: Was ist der Sinn dieser Klasse?)



  • Es geht um eine Klasse, die eine Handvoll Histogramme aus dem ROOT framework zusammen in ein Fenster zeichnet. Die dafuer zustanedige Methode Plot() erzeugt einen Canvas (die ROOT-Version eines Zeichenblattes) in einer vordefinierten Groesse, teilt diesen Canvas in zwei Teile (genannt Pads, ich plotte die Histogramme einmal in linearer und einmal in logarithmischer Darstellung), nimmt eine ganze Reihe Einstellungen bezueglich der Zeichenstile usw. vor und plottet schliesslich die Histogramme nacheinander in die beiden Pads.
    Der allgemeine Algorithmus dafuer ist also statisch, nur die Einstellungen die vorgenommen werden bezueglich der Plotstile sollen von klienten ueberschrieben werden koennen. Diese Einstellungen werden an verschiedenen Objekten vorgenommen (z.B. Linienarten an den Histogrammen, Hintergrundfarbe der Pads an den Pads usw.), daher braucht der Klient Zugriff auf die Histogramme. Ich kann leider keine Methoden zur verfuegung stellen, die einfach die Aufrufe an die Histogramme weiterleiten, da es fuer die Histogramme zig verschiedene Methoden gibt, die alle wichtig sien koennen, und ich in den naechsten Wochen noch was anderes machen moechte 😉 Reduzierter Code bisher:

    TPlot.h:

    #ifndef TPlot_h
    #define TPlot_h
    #include <memory>
    
    #ifndef __CINT__
    typedef double Double_t;
    #endif
    
    class TH1;     //Histogramm-Klasse
    class TAxis;   //Histogramm-Achsen
    class TCanvas; //Zeichenfeld
    class TString;
    class TLegend; //Legende fuer Zeichnungen
    
    class TPlot {
    private:
      //hide copies
      TPlot& operator= (TPlot const&);
      TPlot(TPlot const&);
      //============
    
      /* Viel privates... */
    
    public:
      TPlot(TH1* data, TH1* direct, TH1* resolved, bool log_b = true, bool lin_b = true);
      virtual ~TPlot();
    
      void ShowLinear(bool);
      void ShowLog(bool);
    
    #ifdef __CINT__  
      void ShowLegends(unsigned char flags = 3 );
    #else //CINT kommt mit den Shiftoperatoren nicht klar
      void ShowLegends(unsigned char flags = 1<<0 | 1<<1 );
    #endif
    
      void Plot();
      void Print(TString const& psname);
    
      class BadInstantiated {};
    
    protected:
      std::auto_ptr<TH1> data_;
      std::auto_ptr<TH1> direct_;
      std::auto_ptr<TH1> resolved_;
      std::auto_ptr<TH1> mcall_;
    
      virtual void SetGlobalStyles() const;
      virtual void SetXaxisStyles(TAxis* xaxis);
      virtual void SetYaxisStyles(TAxis* yaxis);
      virtual void SetHistoStyles();
      virtual void SetPadStyles();
      virtual void SetLegendStyles(TLegend* legend);
    };
    
    #endif
    

    TPlot.cxx

    /* andere Methoden */
    void TPlot::Plot()
    {
      if (plotgood()) return;
      if(!lin && !log) {
        cerr << "Error in <TPlot::Plot()>: Both linear and logarithmic set to false - nothing to plot.\n";
        return;
      }
    
      TH1* const maxhist = 
        (mcall_->GetMaximum() > data_->GetMaximum()) ? mcall_.get() : data_.get();
    
      //Set plot styles
      SetGlobalStyles();                     //AUFRUF 
      SetXaxisStyles(maxhist->GetXaxis());   // DER
      SetYaxisStyles(maxhist->GetYaxis());   //VIRTUELLEN
      SetHistoStyles();                      //FUNKTIONEN
    
      //make canvas
      const int nxpads = lin&&log ? 2 : 1;
      const int canvwidth = 500*nxpads;
      const int canvheight = 300;
      canvas_.reset(new TCanvas);
      canvas_->SetCanvasSize(canvwidth, canvheight);
      canvas_->SetWindowSize(canvwidth+50, canvheight+50);
      canvas_->Divide(nxpads, 1);
    
      //draw pads
      for (int i=1; i <= nxpads; ++i) {
        bool islog = (!lin || i==2);
        canvas_->cd(i);
        SetPadStyles();                //NOCH EINE VIRTUAL
        if(islog) gPad->SetLogy(); 
        maxhist->Draw("AXIS");
        data_->Draw("P SAME");
        resolved_->Draw("SAME");
        direct_->Draw("SAME");
        mcall_->Draw("SAME");
        //legends
        if(!islog && legendlin) linleg_.reset(MakeLegend(xlegendlin, ylegendlin));
        if(islog && legendlog) logleg_.reset(MakeLegend(xlegendlog, ylegendlog));
      }
    }
    
    void TPlot::SetHistoStyles() {  //ZUGRIFF AUF DIE PROTECTED MEMBER
      //set plot styles for data
      data_->SetMarkerStyle(19);
      data_->SetMarkerSize(1);
    
      //set plot styles for mcdir
      direct_->SetLineStyle(2);
    
      //set plot styles for mcres
      resolved_->SetLineStyle(3);
    
      //set plot styles for combined mcres & mcdir
    }
    


  • Oh man, ich hab ne recht simple Methode gefunden: Ich aendere die Signatur der virtuellen Methoden und uebergebe ihnen Referenzen/Zeiger auf die noetigen Elemente. Damit haben nur die Methoden den Zugriff. Hab ich bei SetLegendStyle adoch auch schon gemacht *Kopf->Tisch*

    void TPlot::Plot() {
      /* ... */
      SetHistoStyles(data_.get(), direct_.get(), resolved_.get(), mcall_.get());
      /* ... */ 
    }
    
    void TPlot::SetHistoStyles(TH1* data, TH1* direct, TH1* resolved, TH1* mcall) {  //ZUGRIFF AUF DIE PROTECTED MEMBER
      //set plot styles for data
      data->SetMarkerStyle(19);
      data->SetMarkerSize(1);
    
      //set plot styles for mcdir
      direct->SetLineStyle(2);
    
      //set plot styles for mcres
      resolved->SetLineStyle(3);
    
      //set plot styles for combined mcres & mcdir
    }
    


  • Dann merkt sich wer die Zeiger in der Klasse, uns du hast wieder dasselbe Problem.

    Mach P doch einfach zum friend, oder leite P private von T ab, oder verwende using um die Sichtbarkeit der Integerlis aus T in P auf private zu verändern.


Anmelden zum Antworten