PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem mit Zugriffstyp in C++


Neomi
2007-07-07, 00:30:06
Für eine saubere Kapselung brauche ich den Zugriffsmodus "protected" in manchen Klassen, aber in wenigen Situationen funktioniert das einfach nicht so wie erwartet. Hier erstmal das Kernproblem:

class A
{
protected:
int a;
A *pa;

public:
A() : a(0), pa(NULL) {}
};

class B : A
{
public:
B()
{
a = 1; // Zugriff über this, funzt
pa = this; // Zugriff über this, funzt

a = ((B*) pa)->a; // Unschöner Hack, funzt
a = pa->a; // Fehler
}
};

Wenn ein Objekt auf geschützte Variablen der Basisklasse zugreifen will, dann geht das über den this-Pointer, weil der natürlich den Typ der eigenen Klasse hat. Mit einem Pointer, der auf die eigene Klasse typisiert ist, kann man dann auch auf die geschützten Variablen der Basisklasse von anderen Objekten zugreifen. Mit einem Pointer, der den Typ der Basisklasse hat, geht das aber auf einmal nicht mehr. Definitiv betroffen sind VS 2002 und 2005.

Ich habe zwar jetzt mehrere Möglichkeiten, das Problem zu umgehen, davon gefällt mir aber keine:
1. die entsprechenden Variablen in der Basisklasse auf "public" setzen (bäh, die sollen schon geschützt sein)
3. Basisklasse mit "friend"-Deklarationen fluten (auch blöd, das Interface sollte nicht für jede Erweiterung angerührt werden müssen)
2. Pointer umcasten, obwohl es ein ganz anderer Typ sein kann (bäh, potentiell gefährlich und sieht im Code doof aus)

Kennt jemand eine saubere Lösung für dieses Problem? Warum tritt das überhaupt auf?

del_4901
2007-07-07, 01:01:20
Du benutzt da auch private Vererbung ... bist du sicher, dass das gewollt ist?

probier mal:

class B : public A
{
};


EDIT:

class B : potected A
{
};

könnte (vllt) auch gehn

Ich glaube das knallt auch blos, weil du das im Konstruktor verwendest. In einer Standartmethode sollte das auch mit private gehn.

del_4901
2007-07-07, 01:13:48
BTW: Weil ich das gerade mit dem cast seh (um um Coda das Wort vorwegzunehmen) ... warum nimmst du keinen static_cast<> ?

Neomi
2007-07-07, 01:45:34
Die private Vererbung macht nur einen Unterschied, wenn man von außen über die abgeleitete Klasse auf die Basisklasse zugreifen will. Für den Zugriff innerhalb hilft auch kein public. Ob ich im Konstruktor oder in einer Methode darauf zugreifen will, macht auch keinen Unterschied.

Die Sache mit dem static_cast ist zwar hübscher, aber genauso potentiell gefährlich wie mit dem Cast im Beispiel. Ich kann damit aus dem A* einen B* machen, obwohl z.B. ein Objekt der Klasse C (class C : public A, hat also nichts mit B zu tun) dahinter steckt. Darauf wird es wohl hinauslaufen, wenn es keine wirklich saubere Lösung gibt.

Trap
2007-07-07, 10:19:18
Warum tritt das überhaupt auf?
Weil es im C++ Standard so definiert ist.

Siehe 11.5 in http://www.kuzbass.ru:8086/docs/isocpp/access.html

Neomi
2007-07-07, 15:45:20
Weil es im C++ Standard so definiert ist.

Einerseits eine sinnlose Einschränkung, andererseits die logische Konsequenz aus nötigen anderen Einschränkungen. Naja, da kann man wohl nichts machen. Ich habe jetzt im betroffenen Interface die üblichen öffentlichen Methoden zur Abfrage hinzugefügt. Damit können zwar Interna von überall aus ausgelesen werden, die nicht öffentlich sein sollten, aber immerhin bleibt es aufs Auslesen beschränkt.

Xmas
2007-07-07, 16:10:17
Du könntest zu A einen protected Getter hinzufügen der pa->a zurückgibt.

Neomi
2007-07-07, 20:13:36
Ansich eine gute Idee, aber da kämen dann noch einige weitere Getter hinzu. Ich bräuchte noch pa->pa->a, pa->pa->pa->a, ... Das ginge zwar noch über einen einzelnen Getter GetA(int) mit einen Index zum mitzählen, aber damit hätte ich dann beim Durchlauf der Liste auf einmal eine quadratische statt einer linearen Laufzeit, weil ich nicht beim vorherigen Ergebnis weitermachen kann. Da ist der öffentliche Getter doch das kleinere Übel.

Edit:
Aaah, andere Idee auf deiner basierend...

class A
{
protected:
int a;
A *pa;

int get_a(A *that) { return(that->a); }
A* get_pa(A *that) { return(that->pa); }

public:
A() : a(0), pa(NULL) {}
};

Auf die Art funktioniert dann wohl alles und ist trotzdem geschützt. :)