Forum: Mikrocontroller und Digitale Elektronik C++ Referenz Lifetime in Konstruktor


von Cplusplusser (Gast)


Lesenswert?

Hallo,

bin kürzlich auf ein Problem gestoßen, dass ich mir vermutlich erklären 
kann aber ich keine Lösung habe. Folgender Code:
1
#include <stdio.h>
2
3
class TestConfig
4
{
5
public:
6
    TestConfig(int const param1, int const param2) : param1(param1), param2(param2) {};
7
8
    int param1;
9
    int param2;
10
};
11
12
class Test
13
{
14
public:
15
    Test(TestConfig const &conf) : conf(conf) {};
16
    
17
    void printConf(void)
18
    {
19
        printf("%d %d\n", this->conf.param1, this->conf.param2);
20
    }
21
22
protected:  
23
    TestConfig const &conf;
24
};
25
26
27
int main()
28
{
29
    // OK
30
    TestConfig myConfig(1, 20);
31
    Test myTest(myConfig);
32
    
33
    // Wrong
34
    Test myTestWrong(TestConfig(2,30));
35
    
36
    myTest.printConf();       // 1 20
37
    myTestWrong.printConf();  // 0 0 
38
39
    return 0;
40
}

Der Code kann unter https://www.onlinegdb.com/online_c_compiler getestet 
werden.

Das Problem ist, dass nur im ersten Fall die this->conf Referenz auf 
eine gültige Instanz zeigt. Der Grund hierfür ist soweit ich das jetzt 
verstanden habe dass die Lebenszeit der Conf Instanz nach dem Aufruf des 
Compilers vorbei ist. Ist das korrekt? Wenn ja, kann ich das irgendwie 
verhindern oder ist das einfach falsche Benutzung von C++ des Callers? 
Oder wie könnte man das eleganter lösen, damit so ein Problem gar nicht 
auftreten kann?

von nicht"Gast" (Gast)


Lesenswert?

Bei mir gehts korrekt. Wie Kompilierst du das denn?

von Dr. Sommer (Gast)


Angehängte Dateien:

Lesenswert?

Cplusplusser schrieb:
> Test myTestWrong(TestConfig(2,30));
Die "TestConfig"-Instanz verschwindet nach dieser Zeile sofort, weshalb 
die Referenz myTestWrong.conf ins Nirwana zeigt. Daher ist dieser Code 
fehlerhaft.

Die einfachste Lösung ist, es so zu benutzen wie du es bereits bei "OK" 
richtig gemacht hast. Alternativ könntest du es mit dynamischer 
Speicherverwaltung machen (siehe Anhang), oder mit shared_ptr. Merke: 
Referenzen in C++ sind fast gleich zu Zeigern, mit dem Unterschied dass 
man das Ziel nicht ändern kann, und man bei der Erstellung auch ein Ziel 
angeben muss.

PS: Warum verwendest du den C-Header <stdio.h> und nicht den C++-Header 
<cstdio>? Warum verwendest du überhaupt printf und nicht die sichereren 
iostreams?

von C++ (Gast)


Lesenswert?

Entweder du speicherst eine Kopie in der Klasse.
1
protected:  
2
    TestConfig conf;

Oder einen Pointer, wenn du nicht willst dass eine Kopie erstellt wird:
1
protected:
2
    const TestConfig* conf;

Dann kannst du mit new ein Object anlegen:
1
    Test myTestWrong(new TestConfig(2,30));

Du musst aber auch darauf achten es wieder mit delete zu löschen wenn du 
es nicht mehr brauchst. Zum Beispiel im Destruktor von Test.

von Cplusplusser (Gast)


Lesenswert?

nicht"Gast" schrieb:
> Bei mir gehts korrekt. Wie Kompilierst du das denn?
Kompillieren ja, nicht aber Ausführen.

Dr. Sommer schrieb:
> Cplusplusser schrieb:
>> Test myTestWrong(TestConfig(2,30));
> Die "TestConfig"-Instanz verschwindet nach dieser Zeile sofort, weshalb
> die Referenz myTestWrong.conf ins Nirwana zeigt. Daher ist dieser Code
> fehlerhaft.

Genau, wie ich es mir auch erklärt habe.

> Die einfachste Lösung ist, es so zu benutzen wie du es bereits bei "OK"
> richtig gemacht hast. Alternativ könntest du es mit dynamischer
> Speicherverwaltung machen (siehe Anhang), oder mit shared_ptr. Merke:
> Referenzen in C++ sind fast gleich zu Zeigern, mit dem Unterschied dass
> man das Ziel nicht ändern kann, und man bei der Erstellung auch ein Ziel
> angeben muss.

Dynamisch scheidet leider aus.

>
> PS: Warum verwendest du den C-Header <stdio.h> und nicht den C++-Header
> <cstdio>? Warum verwendest du überhaupt printf und nicht die sichereren
> iostreams?

War im Beispiel bei www.onlinegdb.com schon drin, deshalb habe ich es 
gelassen, da es nur um die Klassen ging.

C++ schrieb:
> Entweder du speicherst eine Kopie in der Klasse.
1
protected:
2
     TestConfig conf;

Das ist vermutlich die sicherste Lösung. Dann wird allerdings immer 1x 
der Defaultconstructor aufgerufen (den es u.U. nicht gibt/geben soll), 
das würde ich gerne vermeiden. Kann man das nicht irgendwie verhindern, 
dass man bspw. die Instanz innerhalb der Klasse  durch eine Kopie der 
übergebenen Instanz initialisiert?

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.