avr-g++ kennt new-Operator nicht

Gast #3168418
Lesenswert?

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!
#3168567
Lesenswert?

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
#3168703
Lesenswert?

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.
Gast #3168709
Lesenswert?

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?
#3168737
Lesenswert?

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.
#3168887
Lesenswert?

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
Gast #3168931
Lesenswert?

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 ...
#3169025
Lesenswert?

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)
#3169042
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren