c++ static class mit und ohne Konstructor

Gast #6177549
Lesenswert?

Hi Leute,
hab eine Frage zu C++.

Ist der default Konstrucktor an, dann ist das Programm nur 1004 B groß.
Memory region         Used Size  Region Size  %age Used
        MFlash32:        1004 B        32 KB      3.06%
         RamLoc8:          40 B         8 KB      0.49%

Aktiviere ich meinen Konstrucktor wächst die Größe gleich auf 10952 B
Memory region         Used Size  Region Size  %age Used
        MFlash32:       10952 B        32 KB     33.42%
         RamLoc8:          80 B         8 KB      0.98%

Ich verstehe den Grund nicht...

Wo finde ich eine Erklärung dazu?
1
class test {
2
public:
3
    int get(){
4
       return x;
5
    }
6
    static test *inst(){
7
        static test x;
8
        return &x;
9
    }
10
    void set(int v){
11
        x = v;
12
    }
13
// mit Konstrucktor 10952 B code size
14
// mit default konstrucktor 1516 B
15
    test(){};
16
private:
17
        int x;
18
};
19

20
int main( void )
21
{
22
    test::inst()->set(7); 
23
return 0;
24
}
benutze den gnu c++ 14
Gast #6177591
Lesenswert?

AVR-gcc 9.2 C++17 -Os
1
class Test
2
{
3
public:
4
  int get()
5
  {
6
    return x;
7
  }
8
  static Test &instance()
9
  {
10
    static Test x;
11
    return x;
12
  }
13
  void set(int x)
14
  {
15
    this->x = x;
16
  }
17
  // mit Konstruktor 180 Bytes code size
18
  // mit default konstrucktor 138 Bytes
19
  //Test() {};
20
private:
21
  int x;
22
};
23

24
int main( void )
25
{
26
  Test::instance().set(7);
27
  return 0;
28
}

Ohne Konstuktor wird das ganze Klassengedöns weg optimiert
Komplett

Mit Konstruktor macht der Kompiler alles inline.
Es wird kein zusätzliches call/ret generiert.

Kann man sic hauch schöne mit dem Disassembler anschauen.
Persönliche Seite #6177642
Lesenswert?

pointer->wald schrieb:
> static test x;

Die Initialisierung von statischen lokalen Variablen erfordert 
Thread-Synchronisation, welche sich anscheinend beim default-Konstruktor 
wegoptimieren lässt. Wenn 2 Threads gleichzeitig "inst" aufrufen muss 
sichergestellt werden, dass die "x" Variable nur exakt 1x initialisiert 
wird; da der eigene Konstruktor beliebigen Code enthalten kann, geht das 
nur über Mutexe. Dass dein Controller ggf. gar kein RTOS mit Threads 
nutzt weiß der Compiler nicht.

Die Lösung ist ganz schlicht eine Klassen-statische Variable zu 
verwenden:
1
class test {
2
  private:
3
    static test instance;
4
  public:
5
    static test& inst() {
6
        return instance;
7
    }
8
};

Der Nachteil daran ist, dass der Konstruktor immer aufgerufen wird, auch 
wennn "inst" niemals aufgerufen wird; dafür kann man den Konstruktor 
auch ggf. "constexpr" machen, was die Initialisierung noch effizienter 
macht.

Es ist auch besser eine Referenz statt eines Pointers zurückzugeben.
Gast #6177663
Lesenswert?

Arduino Fanboy D. schrieb:
> Ohne Konstuktor wird das ganze Klassengedöns weg optimiert
> Komplett

Frage:

Würde bedeuten dass ich ohne existierenden Konstruktor keine
zweite (oder weitere) Instanz(en) anlegen kann? Denn bei
mehreren Instanzen brauche ich ja "Klassengedöns" (?), sonst
würde ja für jede Instanz neuer Code angelegt (werden müssen)(?).
Persönliche Seite #6177671
Lesenswert?

hmmmm schrieb:
> Würde bedeuten dass ich ohne existierenden Konstruktor keine
> zweite (oder weitere) Instanz(en) anlegen kann?

Doch klar. Es ist ja doch einer vorhanden, der wird automatisch erzeugt. 
Ohne Konstruktur könntest du überhaupt keine Instanz anlegen!

hmmmm schrieb:
> Denn bei
> mehreren Instanzen brauche ich ja "Klassengedöns" (?), sonst
> würde ja für jede Instanz neuer Code angelegt (werden müssen)(?).

Code wird nur pro Klasse erzeugt, nicht pro Instanz. Keine Ahnung was 
"Klassengedöns" ist und wie da was von wegoptimiert werden kann...
Gast #6177681
Lesenswert?

Niklas G. schrieb:
> Code wird nur pro Klasse erzeugt, nicht pro Instanz.

Das ist mir schon klar. War auch bereits Inhalt meiner Frage.

Niklas G. schrieb:
> Keine Ahnung was
> "Klassengedöns" ist und wie da was von wegoptimiert werden kann...

Unser allwissender Fanboy behauptet jedenfalls sowas.

Unter seinem "Klassengedöns" habe ich verstanden dass das Handling
vorhanden ist für mehrere Instanzen ein und denselben Code zu
verwenden wie es normalerweise implementiert ist.
Gast #6177698
Lesenswert?

Niklas G. schrieb:
> Keine Ahnung was
> "Klassengedöns" ist und wie da was von wegoptimiert werden kann...

Das asm Listing "ohne Konstuktor" zeigt, dass alles was die 
Klasse/Instanz betrifft, nicht im generierten Code erscheint.
Es wird komplett weg optimiert.
Mit Stumpf und Stiel.

Ein Programm nur mit:
1
int main( void )
2
{
3
  return 0;
4
}
Erzeugt exakt den gleichen Maschinencode.
Gast #6177728
Lesenswert?

Arduino Fanboy D. schrieb:
> Es wird komplett weg optimiert.
> Mit Stumpf und Stiel.

Dann erklär mal bitte wie mehrere Instanzen einer Klasse
ohne "Klassengedöns" auskommen sollen.

Gut, du kannst dich rausreden indem du dein "Klassengedöns"
nach deinem Belieben definierst.

Ich habe es nun mal so aufgefasst wie von mir beschrieben.
Persönliche Seite #6177759
Lesenswert?

Arduino Fanboy D. schrieb:
> Es wird komplett weg optimiert.
> Mit Stumpf und Stiel.

Weil die Klasse halt nicht wirklich verwendet wird. Auch eine Klasse 
ohne expliziten Konstruktor kann "normalen" Klassen-Spezifischen-Code 
erzeugen, also z.B. eigenständige Member-Funktionen.

Markus schrieb:
> Kurze Zwischenfrage: Warum bietet sich hier eher eine Referenz an?

Ein Zeiger sagt dem Aufrufer, dass er auch Null sein kann, und er eben 
auf Null prüfen muss. Eine Referenz kann nicht Null sein, weshalb man 
dort keine Prüfung machen muss. Im konkreten Fall wird nie Null 
zurückgegeben, also kann man auch eine Referenz nehmen.
Gast #6177810
Lesenswert?

hmmmm schrieb:
> Dann erklär mal bitte wie mehrere Instanzen einer Klasse
> ohne "Klassengedöns" auskommen sollen.
Das war kein Thema im Eingangsposting.

Da dreht es sich nur um die unterschiedlichen Codegrößen, in 
Abhängigkeit von der Anwesenheit eines eigenen Konstruktors.
Gast #6178056
Lesenswert?

hmmmm schrieb:
> Arduino Fanboy D. schrieb:
>> Das war kein Thema im Eingangsposting.
>
> ja neee .... iss klaa

Ich beziehe mich auf das Eingangsposting und mein "Experiment" dazu.
Wenn du über irgendwas anderes schwadronieren möchtest, nur zu, aber 
halt mich bitte da raus.
#6180872
Lesenswert?

Einfach mit -fno-threadsafe-statics kompilieren, dann kann man statische 
Klassen auf dem AVR effektiv nutzen.

Die Erklärung für diesen Switch:
Do not emit the extra code to use the routines specified in the C++ ABI 
for thread-safe initialization of local statics. You can use this option 
to reduce code size slightly in code that doesn’t need to be 
thread-safe.

https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-Options.html

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