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.