Hallo,
ich habe in C++ einige Hardwarezugriffe auf einen AtMega128 in Klassen
verteilt. Diese Klassen enthalten nur Methoden und keine Daten. Leider
ist es so, dass unter C++ jede Klasse, auch dann wenn Sie praktisch
keine Daten enthält, mindestens 1 Byte im Speicher belegt (um Objekte
dieser Klasse in einem Array unterscheiden zu können).
Das mag auf einem PC sicher weniger schlimm sein, aber auf einem µC sehe
ich es absolut nicht ein, pro Klasse ein Byte spendieren zu müssen,
obwohl die Klasse nur Methoden zur besseren Strukturierung und
Abstraktion der Hardware enthalten. Vor allem bei einer etwas größeren
Verschachtelung dieser führt das schnell zu viel Speicherverbrauch der
theoretisch gar nicht nötig ist.
Also habe ich ausprobiert, anstatt ein Objekt dieser Hardware-Klasse
anzulegen einfach ein Objekt über "extern" zu deklarieren. Dieses Objekt
wird allerdings in keiner anderen später gelinkten Datei wirklich
definiert. Ich sage dem Compiler also: "Hey da gibt es ein Objekt, ich
weiß nicht wo, aber es sieht so und so aus."
Siehe da, ich habe auf alle Methoden dieser Klasse Zugriff und auch auf
die Methoden der Unterklassen und das ohne, dass Speicherplatz dafür
reserviert werden muss.
Die Frage die sich nun stellt: Ist dies eher ein schmutziger Workaround
im avr-g++ oder, der womöglich in späteren Versionen nicht mehr
funktionieren wird oder ist so etwas in C++ grundsätzlich zulässig und
kann dann auch so verwendet werden?
Beispielcode:
Du koenntest auch NULL auf den Klassentyp casten und darueber zugreifen
- aber das ist genauso Pfusch ;-)
Was du eigentlich willst, ist, die Methoden als static deklarieren und
sie dann per KlassenName::MethodenName( ) aufrufen.
Oder du machst das mit Namespaces und Funktionen - so viele Namespaces,
wie du dann braeuchtest, wuerde ich aber nicht einsetzen wollen.
Btw, geschweifte Klammern gehoeren IMO uebereinander - sie markieren
Anfang und Ende eines Blocks und sollten den Block leicht erkennbar
machen.
Hallo Peter,
die Idee mit dem Pointer ist eigentlich super. Das dürfte auch auf
andere Compiler übertragbar sein und ist absolut kein Problem solange
eben kein Speicherzugriff stattfindet.
Bei dem "extern" Trick besteht halt noch der Vorteil, dass der Linker
meckert sobald ein Speicherzugriff stattfindet, weil er dann die
dazugehörige Speicherstelle nicht finden kann.
Ich habe das gerade mal beim Visual C++ 2008 Compiler ausprobiert: Die
extern-Variante nimmt er nicht (Linker kann das Symbol nicht finden),
die mit dem Null-Pointer schon.
Die Variante mit dem static geht nicht, da in der Klasse weitere Klassen
(ohne Daten-Member)geschachtelt sind. Diese "objekte" müssten dann auch
static sein und würden in diesem Moment ebenfalls ja 1 Byte belegen. Ich
hätte also nur das eine Byte der obersten Klasse gespart.
Namespace funktioniert nicht, da es sich um Templateklassen handelt und
eine solche Funktionalität meiner Meinung nach nicht mit Namespaces
nachgestellt werden kann.
Und das mit den Klammern ist eben Ansichtssache ;-)
Ja begeistern tun mich beide Lösungen auch nicht. Die eine scheint nicht
portierbar zu sein und wer weiß wie es mit der nächsten Version des
avr-g++ aussieht. Die andere ist zwar portierbar birgt aber das Risiko
auf einen ungewollten Speicherzugriff (falls man aus Versehen doch mal
ein Datenelement in einer der Klassen definiert).
Das leere Klassen immer ein Byte groß sind ist ja eine reine
Definitionssache des Compilers um eventuelle andere Probleme umgehen zu
können.
Mir wäre es am liebsten, wenn man den Compiler anweisen könnte für
solche Klassen keinen sinnlosen Speicher zu reservieren. Das sind
immerhin einige etliche Byte die effektiv nicht genutzt werden... *Kopf
schüttel*
Peter Stegemann schrieb:
> Was du eigentlich willst, ist, die Methoden als static deklarieren und> sie dann per KlassenName::MethodenName( ) aufrufen.
Genau, so hätte ich's auch gemacht. Ein Objekt von einer leeren Klasse
zu instanziieren verwirrt doch mehr als es nützt. Der Compiler sollte
das nicht mit 1 Byte bestrafen, sondern mit 1 GByte ;-)
A. N. schrieb:
> Die Variante mit dem static geht nicht, da in der Klasse weitere> Klassen (ohne Daten-Member)geschachtelt sind.
Das verstehe ich nicht. Man kann doch wunderbar Methoden auch in
verschachtelten Klassen aufrufen:
Naja, ganz so simpel ist die Struktur dahinter nicht. Das ganze
entspricht eher diesem Beispiel hier und da sehe ich keine Möglichkeit
ohne "Pseudoobjekt" sinnvoll auf die Methoden zugreifen zu können.
Die Idee mit den typedefs ist sehr gut, so werde ich es wohl machen...
Danke yalu!
Nun zu deiner Frage: Wenn die eigentliche Variation der Klassen nicht in
Datenobjekten stecken sondern im Templateparameter N, dann besitzt die
Klasse keine Datenobjekte sondern nur Methoden, es ist also eine leere
Klasse.
> ... es ist also eine leere Klasse.
Dass Klassen ohne Datenelemente sinnvoll sein können, ist schon klar.
Ich kann mir nur nicht vorstellen, dass es Fälle gibt, wo es sinnvoll
oder sogar notwendig ist, von solchen Klassen Objekte zu instanziieren,
da ein Objekt ohne Daten meiner Meinung nach nicht mehr ist/kann als
seine Klasse.
Nachdem du die "Wegoptimierung" der Objekte a0 bis a5 durch die Typedefs
akzeptiert hast, hat sich das Thema aber wohl weitgehend erledigt.
Naja, es ist eben der nächste logische Schritt: Man programmiert eine
Klasse mit all seinen Methoden und um diese zu benutzen legt man eben
ein Objekt dieser an. Der einzige Grund, warum man das nicht machen kann
ist doch die Definition in den C++ Standard, dass jedes Objekt
mindestens ein Byte groß ist, auch wenn dieses gar nicht im Speicher
existiert da es keinen Speicher belegt. Wäre das nicht so definiert,
dann spräche doch nichts dagegen mit leeren Klassen genauso zu verfahren
als wie mit vollen Klassen.
Ich meine das sieht ja auch im Quellcode komisch aus, wenn man auf die
einen Methoden über den "." Operator zugreift und auf andere Methoden
über ein "::". Dem Programmierer, der am Ende mit den Klassen umgehen
muss, ist es doch egal ob da Daten drin gespeichert werden und daher
Objekte angelegt werden müssen oder ob es leere Klassen sind, die daher
ohne Objekte verwendet werden.
Das verletzt ja auch das Prinzip der Kapselung: Einmal ein Projekt mit
solchen Klassen realisiert, kann ich mich nicht mehr im Nachhinein
entscheiden doch noch ein Datenobjekt in eine der Klassen zu
integrieren, dann müsste man nämlich doch wieder ein Objekt dieser einen
Klasse anlegen und damit den ganzen Quellcode umbauen. Könnte man
ungestraft ein Objekt einer leeren Klasse anlegen, dann wäre das gar
kein Thema. Aber was soll man machen... Standard ist Standard.
Danke nochmal für deine Lösung... manchmal sind die einfachsten
Varianten die auf die man gar nicht kommt.
Oh, ich merke gerade, dass ich das doch nicht so realisieren kann. Ich
nutze in den Klassen sehr massiv überladene Operatoren und die dürfen
nicht static sein.
Also bleibt nichts anderes übrig als doch mit einem Pseudoobjekt zu
arbeiten.