Hallo, ich versuche gerade ein kleines Programm in c++ für den AVR zu testen. Leider kennt der g++-Compiler den new-Operator nicht. Wieso ist das so?
achso, die Fehlermeldung lautet: " undefined reference to `operator new(unsigned int)'
vermutlich weil die Dateiendung ".c" und nicht ".cpp" ist. Ansonsten dem Compiler via " -x c++ " mitteilen dass es sich um C++ handelt.
Ich schätze mal, da wird sich in der Zwischenzeit nicht viel verändert haben Beitrag "c++ new und delete"
Noop schrieb: > achso, die Fehlermeldung lautet: > > " undefined reference to `operator new(unsigned int)' "undefined reference" ist eine Fehlermeldung vom Linker, der aus dem bereits compilierten Einzelteilen dann das komplette Programm zusammenbaut.
Timmo H. schrieb: > vermutlich weil die Dateiendung ".c" und nicht ".cpp" ist. Ansonsten dem > Compiler via " -x c++ " mitteilen dass es sich um C++ handelt. Danke für die Antwort. Leider hat beides nicht geholfen.
Du erzeugst ein Objekt. Deshalb die Klammern nicht vergessen!
1 | b = new CRectangle(); |
Grüsse
Markus M. schrieb: > Du erzeugst ein Objekt. Deshalb die Klammern nicht vergessen! Nö. Es gibt zwar einen klitzekleinen subtilen Unterschied ob mit Klammern oder ohne, aber die meisten C++ Programmierer werden da nie drüber stolpern, wenn sie sich an ein paar Basisregeln halten.
Ich habe die Klammer hinzugefügt. Dennoch will er nicht. Der MinGW compiliert dieses Beispiel problemlos durch. Vielleicht kann der AVR-g++ nicht alle Mechanismen von c++?
Noop schrieb: > Vielleicht kann der AVR-g++ > nicht alle Mechanismen von c++? Korrekt, "richtiges" C++ macht auf einem µC auch nicht wirklich sinn (wenig RAM). Darum sind nur die wichtigsten Dinge implementiert. Ansonsten die Implementierung von Karl Heinz Buchegger verlinkten Thread verwenden
Karl Heinz Buchegger schrieb: > Beitrag "c++ new und delete" Ok. Habe erst jetzt Deinen Beitrag gesehen. Ich lese mir das durch.
Timmo H. schrieb: > Korrekt, "richtiges" C++ macht auf einem µC auch nicht wirklich sinn > (wenig RAM). Ja, da gibt es kontroverse Ansichten. Zu dieser Diskussion wollte ich gar nicht. Ich habe einfach nur lust das mal auszuprobieren.
Dann musst Du noch die "stdc++" Library dazu linken. g++ -o <program> <program>.cpp -lstdc++ Grüsse
Karl Heinz Buchegger schrieb: > Beitrag "c++ new und delete" Ok. Der Thread hat geholfen. Mit dem Patch funktioniert es nun. Allerdings sind mehrdimensionale Geschichten wie : CRectangle * d = new CRectangle[2]; nicht möglich. Das macht er dann nicht mit. Markus M. schrieb: > g++ -o <program> <program>.cpp -lstdc++ zeigt keine Änderung. Aber vielen Dank.
> CRectangle * d = new CRectangle[2]; > nicht möglich. Das macht er dann nicht mit. Natürlich kann das nicht funktionieren, wenn es überhaupt keine "new" Implementierung gibt. Du müsstest nun ebenso ein "new[]" und "delete[]" für Arrays implementieren. Schema wie oben. http://en.cppreference.com/w/cpp/memory/new/operator_new Grüsse
Markus M. schrieb: > Natürlich kann das nicht funktionieren, wenn es überhaupt keine "new" > Implementierung gibt. Jo! Danke! ich habe mal die New-Implentierung überladen und schon geht es auch mit Arrays: void * operator new(size_t size) { return malloc(size); } void* operator new[](size_t count/*, placement_params*/) { return malloc(count); } Besten Dank von einem Noop!
Und wie viel Speicher reserviert dir deine "new[]" Implementierung? Ganz sich nicht "count*sizeof(CRectangle)" ;-) Grüsse
Markus M. schrieb: > Ganz sich nicht "count*sizeof(CRectangle)" ;-) Ok. Darüber habe ich auch vorhin nachgedacht. Meinst Du es wird die Länge des ersten Array-Elements reserviert? Hast Du einen Tip?
Ich hab mich gerade mal ein bisschen dahingehend schlau gemachte, wenn "new" und "delete" nicht vorgesehen sind. Normalerweise kümmert sich der Compiler darum, dass eine Klasse über "new", "delete", "new[]" und “delete[]" verfügt. Da der Compiler hier nicht tätig wird, müsstest Du für jede deiner Klassen diese Operatoren selbst implementieren. Am besten Du vergisst "new", wenn es der Compiler nicht unterstützt. Du kannst Objekt Arrays immer noch auf dem Stack ablegen. Das im Beitrag "c++ new und delete" beschrieben "new" ist nicht wirklich ein Ersatz, da nur die Anzahl von Bytes berücksichtigt wird und Datentypen ignoriert werden. Man kann sich bei den einfachen Datentypen mit "sizeof()" behelfen. Bei Klassen funktioniert es aber nicht mehr. Mikrocontroller sind eine Welt für sich. Ich persönlich vermeide Dynamische Daten auf einem MC. Bezieht jetzt sich auf AVR. Grüsse
Markus M. schrieb: > Ich hab mich gerade mal ein bisschen dahingehend schlau gemachte, wenn > "new" und "delete" nicht vorgesehen sind. > > Normalerweise kümmert sich der Compiler darum, dass eine Klasse über > "new", "delete", "new[]" und “delete[]" verfügt. Da der Compiler hier > nicht tätig wird, müsstest Du für jede deiner Klassen diese Operatoren > selbst implementieren. Du verwechselst den Operator new mit dem new Operator. Das sind 2 verschiedene Dinge. So wie Noop das gemacht hat, passt das schon.
Noop schrieb: > Ich habe die Klammer hinzugefügt. Dennoch will er nicht. Der MinGW > compiliert dieses Beispiel problemlos durch. Vielleicht kann der AVR-g++ > nicht alle Mechanismen von c++? Das soll wohl ein Witz sein! "new" ist ja schließlich vollkommen unwichtig oer was?
Kopfschüttler schrieb: > Noop schrieb: >> Ich habe die Klammer hinzugefügt. Dennoch will er nicht. Der MinGW >> compiliert dieses Beispiel problemlos durch. Vielleicht kann der AVR-g++ >> nicht alle Mechanismen von c++? > > Das soll wohl ein Witz sein! > "new" ist ja schließlich vollkommen unwichtig oer was? Nun, offensichtlich ist es niemandem wichtig genug, um sich die Mühe zu machen, einen Patch für g++ einzureichen, der das nachrüstet. Aber du kannst das gerne tun, wenn du möchtest.
Kopfschüttler schrieb: > Das soll wohl ein Witz sein! > "new" ist ja schließlich vollkommen unwichtig oer was? Ich glaube, dazu ist hier alles geschrieben worden: Beitrag "Objektorientierung mit avr gcc" (Ich hatte mich da mit reingehangen. Karl Heinz Buchegger und Rolf Magnus haben mir das Thema mit unendlicher Geduld nahegebracht.)
Danke Ralf, den Thread kannte ich noch nicht. Dort wird das Thema erschöpfend behandelt. Gute Arbeit. @Karl Heinz Buchegger > Du verwechselst den Operator new mit dem new Operator. > Das sind 2 verschiedene Dinge. Ja, da hab ich ein zu weit ausgeholt. Ich dachte für das bessere Verständnis kann ich es so schreiben. Technisch sind es zwei paar Schuhe. > So wie Noop das gemacht hat, passt das schon. Nein, passt nicht. Er gibt "malloc" und "free" nur andere Namen. Grüsse
Hm... also die Überladung funktioniert aber auch mit dem MinGW. Ich habe es dort ausprobiert und es scheint zu funktionieren. Mir fehlt definitiv zu viel Basiswissen. Ich habe mal in der libsupc++ nachgesehen. Die machen das so:
1 | _GLIBCXX_WEAK_DEFINITION void * |
2 | operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc) |
3 | { |
4 | void *p; |
5 | |
6 | /* malloc (0) is unpredictable; avoid it. */ |
7 | if (sz == 0) |
8 | sz = 1; |
9 | p = (void *) malloc (sz); |
10 | while (p == 0) |
11 | { |
12 | new_handler handler = std::get_new_handler (); |
13 | if (! handler) |
14 | _GLIBCXX_THROW_OR_ABORT(bad_alloc()); |
15 | handler (); |
16 | p = (void *) malloc (sz); |
17 | } |
18 | |
19 | return p; |
20 | } |
und für ein Array analog:
1 | _GLIBCXX_WEAK_DEFINITION void* |
2 | operator new[] (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc) |
3 | { |
4 | return ::operator new(sz); |
5 | } |
So habe ich dann einfach mal das gemacht:
1 | void * operator new(size_t size) |
2 | { |
3 | return malloc(size); |
4 | } |
5 | |
6 | void * operator new[](size_t size /*, placement_params*/) |
7 | { |
8 | return operator new(size); |
9 | } |
es scheint auch zu funktionieren. Da mir persönlich Wissen und Erfahrung mit C++ (und grundsätzlich auch in C) fehlt, versuche ich mir das so zu erklären: (Achtung das kann jetzt ein Donnerwetter erzeugen, weil es vielleicht total falsch ist) Der new-operator ist grundsätzlich ein Zeiger auf ein Objekt. Er ruft malloc auf und bekommt von malloc die Speicheradresse des reservierten Speicherplatzes zurück und ordnet diese dann dem ursprünglich zu deklarierenden Object zu: CRectangle * d = new CRectangle[200]; also hier *d. Mir ist nicht klar, woher der Compiler weiß, dass er das Argument (size_t size) die Länge/Größe des Objekts ist. Aber es erscheint mir logisch, dass wenn das funktioniert, es völlig egal ist, ob es ein Array oder ein einzelnes Element ist. Das Objekt hat ja immer eine Endmarkierung im Speicher. So auch ein Array. Naja, weiter reicht mein dummes Hirn nicht ...
Markus M. schrieb: >> So wie Noop das gemacht hat, passt das schon. > Nein, passt nicht. Er gibt "malloc" und "free" nur andere Namen. Und genau das reicht auch schon für den globalen new Operator im einfachsten Fall. Ok, ein bischen Fehlerbehandlung könnte man noch machen, aber mehr ist nicht notwendig. Der Auftrag an den globalen Operator new lautet: besorge Speicher. Mehr braucht er nicht tun. Den Rest erledigt der Aufrufer (wie zb die entsprechenden Objekte in diesem Speicher zu konstruieren)
Noop schrieb: > Der new-operator ist grundsätzlich ein Zeiger auf ein Objekt. Der gloable operator new ist zualerst mal eine Funktion :-) > Er ruft > malloc auf und bekommt von malloc die Speicheradresse des reservierten > Speicherplatzes zurück und ordnet diese dann dem ursprünglich zu > deklarierenden Object zu: Dieser globale operator new hat mit Objekten überhaupt nichts am Hut. Seine Aufgabe ist es Speicher zu besorgen. Nicht mehr und nicht weniger. Was dann mit diesem Speicher weiter passiert, hat ihn nicht zu interessieren. > CRectangle * d = new CRectangle[200]; > > also hier *d. d ist ein Pointer. > Mir ist nicht klar, woher der Compiler weiß, dass er das > Argument (size_t size) die Länge/Größe des Objekts ist. Na der Compiler weiß doch, wie groß ein CRectangle Objekt ist. Du hast das ja vorher in Form der Klassen-Deklaration bekannt geben müssen. Der Compiler hat die analysiert und festgestellt, wieviel Speicher benötigt wird, um 1 Objekt davon zu erzeugen. Und diese Größe übergibt er an den globalen Operator new (oder wie in deinem letzten Beispiel das 200-fache davon), wenn er im Zuge der Objektkonstruktion erst mal den Speicher für dieses Objekt anlegen muss. Operator new besorgt den Speicher und der Compiler sorgt dann dafür, dass ein entsprechender Konstruktor für diesen Speicher aufgerufen wird (oder im Falle eines Arrays eine entsprechende Anzahl an Konstruktoren für jedes Objekt einzeln). Aber damit hat der globale Operator new schon nichts mehr zu tun. Für ihn endet die ganze Sache damit, dass er einen entsprechend großen Speicherbereich organisiert hat. Wie er das macht, dass ist diesem Operator überlassen. Zum Beispiel kann er das mit einem malloc machen. Er könnte aber genausogut auch selbst eine entsprechende Speicherverwaltung in einem vorallokierten Byte-Array machen, wenn das dem Programmierer lieber ist und er das so ausprogrammiert. Oder er könnte an malloc()/free() vorbei sich selbst mit dem Betriebssystem ausschnapsen, wie er an Speicher kommt. Nicht zu verwechseln ist der Operator new mit dem new-Operator. Der globale Operator (wie hier) ist generell dafür zuständig Speicher zu besorgen. Der new-Operator als Klassenmember ermöglichst es einem Programmierer, für eine Klasse eine von der globalen Speicher-Allokier-Strategie abweichende Strategie auf Klassenbasis zu schaffen. Damit kann man zb kleine Objekte, die oft und häufig allokiert werden in einem Art Memory-Pool verwalten, ohne laufend durch die aufwändige dynamische Speicherverwaltung laufen zu müssen. Aber ... das ist eine andere Geschichte. Daru geht es hier nicht. Hier geht es darum, dass es irgendwo im C++ System mal eine unterste Ebene geben muss, die sich um Speicherallokierung und Freigabe kümmert. Genau auf dieser Ebene steigst du ein. Und in deinem Fall delegierst du das eben an die vorhandene Funktion malloc() bzw. free(). > Aber es > erscheint mir logisch, dass wenn das funktioniert, es völlig egal ist, > ob es ein Array oder ein einzelnes Element ist. Im Prinzip: ja. Allerdings hat man mit einem speziellen Operator für die Arrayform, dann als Systemprogrammierer die Möglichkeit, sich für diesen Fall etwas spezielles einfallen zu lassen. Ob man das dann auch benutzt oder nicht, ist eine andere Frage. Der Standardfall ist ganz einfach, dass der Array-Operator einfach den normalen Operator benutzt um seinen Auftrag zu erfüllen. Hier ist einfach nur eine Möglichkeit für etwas 'Flexibilität' eingebaut. Ob man die dann nutzt und wofür man die nutzt, muss der Systemprogrammierer entscheiden. > Das Objekt hat ja immer > eine Endmarkierung im Speicher. Ähm. Nein. > So auch ein Array. Nö. Wie sich die Speicherverwaltung merkt, wie groß der Speicher ist, auf den sie beim malloc einen Pointer rausgerückt hat, das ist das Problem der Speicherverwaltung. C++ kümmert sich da erst mal überhaupt nicht drumm. Der angeforderte Speicher muss die beim Aufruf angegebene Mindestgröße haben. Er kann auch größer sein, aber das Minimum wird beim Aufruf vorgegeben.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.