Forum: PC-Programmierung Überschriben von static constexpr in Ableitung


von Detlef W. (detlefr)


Lesenswert?

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?

1
#include <iostream>
2
3
class Basis {
4
    public:
5
        void show();
6
        constexpr static uint32_t size = 0;
7
};
8
9
void Basis::show() {
10
    std::cout << size << std::endl;
11
}
12
13
class Ableitung : public Basis {
14
    public:
15
        constexpr static uint32_t size = 1;
16
};
17
18
int main() {
19
    Ableitung *ableitung = new Ableitung();
20
    ableitung->show();
21
}

von M.K. B. (mkbit)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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:
1
template<typename D>
2
class Basis {
3
public:
4
    void show() const {
5
        const D* const d = static_cast<const D*>(this);
6
        std::cout << d->size << std::endl;
7
    }
8
    constexpr static uint32_t size = 0;
9
};
10
11
class Ableitung : public Basis<Ableitung> {
12
public:
13
    constexpr static uint32_t size = 1;
14
};
15
16
int main() {
17
    const auto ableitung = std::make_unique<const Ableitung>();
18
    ableitung->show();
19
}

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.

: Bearbeitet durch User
von Sven P. (Gast)


Lesenswert?

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...

von Detlef W. (detlefr)


Lesenswert?

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.

von re (Gast)


Lesenswert?

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)

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von Detlef W. (detlefr)


Lesenswert?

Danke für die Infos :) wieder was gelernt

von re (Gast)


Lesenswert?

Oliver S. schrieb:
> die muß man einfach einmal lernen

Detlef 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)

von M.K. B. (mkbit)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.