Hallo,
ich bin seit geraumer Zeit dabei mich in C++ einzuarbeiten. Ich komme
gerade nicht mehr weiter.
Ich habe eine Klassenkostante size, die ich in der Ableitung mit einem
neuen Wert definiere. Wenn ich von der abgeleiteten Klasse ein Objekt
erstelle und size ausgeben lasse, erhalte ich den Wert aus der
Basisklasse. Das Beispiel unten gibt 0 aus.
Wie erreiche ich, dass ich das von mir erwartete 1 erhalte, am besten
ohne für jede Variable einen virtuellen getter zu schreiben?
Ich glaube ohne virtual kommst du hier nicht aus. Basis::show kennt ja
nur seine Variablen. Es könnten ja auch verschiedene Klassen von Basis
ableiten, aber dadurch würde Basis ja noch den gleichen Code haben. Mit
virtual schaut man dann zur Laufzeit, ob es eine Ableitung gibt.
Wenn dein size Member kein static ist, dann könntest du dem im
Konstruktor von Ableitung einen neuen Wert zuweisen.
Und noch ein Tipp zu deinem Code. In C++ würde ich kein new und delete
verwenden, sondern das ganze in einen Smartpointer tun. In deinen Fall
wäre es ein std::unique_ptr. Dann wird der Speicher immer freigegeben,
egal ob man mit einem Return oder einer exception aussteigt.
Detlef W. schrieb:> Wie erreiche ich, dass ich das von mir erwartete 1 erhalte, am besten> ohne für jede Variable einen virtuellen getter zu schreiben?
Du hast die Mechanik der Vererbung noch nicht verstanden: eine
Basisklasse kennt die von ihr abgeleiteten Klassen nicht.
Man könnte in der Basis-Klasse einen downcast auf Ableitung machen. Dies
ist aber extrem unschön und in C++ nicht möglich.
Man es mit CRTP erreichen:
Dabei habe ich gleich einige absolute Basics korrigiert, die Du Dir auch
nochmal anschauen solltest.
Detlef W. schrieb:>Wie erreiche ich, dass ich das von mir erwartete 1 erhalte, am besten>ohne für jede Variable einen virtuellen getter zu schreiben?
Dein show() oben war kein getter. Zudem vermeiden diesen Begriff: besser
Beobachter oder const-Elementfunktion.
Zudem ist das Vorgehen sinnlos.
static beißt sich mit deiner Anforderung.
Ohne static (und ohne constexpr) könntest du schlicht und einfach eine
Member-Variable im c-tor initialisieren...
Wilhelm M. schrieb:> Du hast die Mechanik der Vererbung noch nicht verstanden: eine> Basisklasse kennt die von ihr abgeleiteten Klassen nicht.
Und das ist etwas was für mich an C++ nicht so schön ist. Einiges
funktioniert nicht wie man es von der Logik her erwarten würde. Wenn ich
eine Ableitung erstelle und diese instaziere, erwarte ich - rein von der
Logik her - das die Instanz zuerst auf die Daten und Methoden der
Ableitung zugreift und erst wenn sie dort nicht verfügbar sind auf die
Elternklasse.
Ich werde dann wohl den Weg gehen, mir über virtuelle Methoden die Werte
zu holen.
Da ich darauf hingewiesen wurde außerden besser Smartpointer zu
verwenden, wie schlägt das beim Speicherverbrauch (Flash / RAM) zu
Buche? Da die Bibliothek auch auf MCs laufen soll, bin ich was das
angeht sehr Vorsichtig.
Detlef W. schrieb:> Smartpointer zu verwenden, wie schlägt das beim Speicherverbrauch [...]
Sei beruhigt:
unique-pointer sind unter der Haube (afaik) ganz gewöhnliche raw-Pointer
und haben somit auch den gleichen Footprint - aber der Compiler weiß von
der Ownership und kann gezielt moven oder den Destruktor aufrufen.
Hat also exakt den gleichen Ressourcenbedarf, nur dass der Compiler hier
eben die Ressourcenfreigabe managt.
Nur shared-pointer schleppen noch u.a. den Verweis auf den
Referenzzähler mit sich herum, sind also ein paar Bytes größer, und
müssen eben noch dieses gemeinsame Verwaltungsobjekt instanzieren.
Kosten also mehr. Aber wenn Du diese Form der Pointer brauchst, musst Du
die betreffende Information sowieso in jedem Fall irgendwo
repräsentieren.
Der echte Vorteil (von beiden Varianten ist), dass man sich nicht mehr
um die delete()s kümmern muss. Korrigiert mich, wenn das inzwischen
veraltet sein sollte.
(re)
re schrieb:> Nur shared-pointer schleppen noch u.a. den Verweis auf den> Referenzzähler mit sich herum, sind also ein paar Bytes größer, und> müssen eben noch dieses gemeinsame Verwaltungsobjekt instanzieren.
Und das ist am effektivsten mit std::make_shared(...) statt eines
std::shared_ptr<A>{new A(...)}. Im modernem C++-Code kommt new/delete
explizit nicht mehr vor.
Detlef W. schrieb:> Ich werde dann wohl den Weg gehen, mir über virtuelle Methoden die Werte> zu holen.
Das sind halt grundlegenden Prinzipien in C++, die lassen sich nicht
durch logisches Nachdenken erahnen, die muß man einfach einmal lernen.
Oliver
Oliver S. schrieb:> die muß man einfach einmal lernenDetlef W. schrieb:> wieder was gelernt
Wenn man mit den Konzepten von Steinzeit-C++ oder von anderen ähnlichen
Sprachen schon vertraut ist, dann ist Stroustrup's "A Tour of C++" ganz
hilfreich, um sich auf den aktuellen Stand zu bringen. Als
Referenzhandbuch oder Lehrbuch viel zu kurz, aber eine sehr wertvolle
Stichpunktsammlung.
(re)
Detlef W. schrieb:> Und das ist etwas was für mich an C++ nicht so schön ist. Einiges> funktioniert nicht wie man es von der Logik her erwarten würde. Wenn ich> eine Ableitung erstelle und diese instaziere, erwarte ich - rein von der> Logik her - das die Instanz zuerst auf die Daten und Methoden der> Ableitung zugreift und erst wenn sie dort nicht verfügbar sind auf die> Elternklasse.
C++ verfolgt das Prinzip, dass man nur für das bezahlt, was man braucht.
Die Basisklasse soll und kann nicht alle Ableitungen kennen. (z.B. weil
diese in einer Bibliothek ist, die Ableitungen aber im Projekt, das die
Bibliothek nutzt).
Um das von dir erwartete Verhalten zu haben, müsste die Basis zur
Laufzeit prüfen, ob es in der Ableitung eine überschriebene Variante
gibt. Dies kostet aber Rechenzeit. Wenn man dieses Verhalten explizit
haben möchte, dann kann man es mit virtual anfordern.
Detlef W. schrieb:> Da ich darauf hingewiesen wurde außerden besser Smartpointer zu> verwenden, wie schlägt das beim Speicherverbrauch (Flash / RAM) zu> Buche? Da die Bibliothek auch auf MCs laufen soll, bin ich was das> angeht sehr Vorsichtig.
Für Einsteiger ist es in C++ tatsächlich schwer zu erkennen, wie teuer
ein Stück Code tatsächlich ist. Zum Beispiel wirst du in deinem
ursprünglichen Code nach dem Compiliern auch den Konstruktor von Basis
und Ableitung nicht mehr finden, weil es nichts zu initialisiern gibt,
auch keinen v-table.