Konstrukter Problem?



  • Ich würde die Memberfunktionen r() und i() aussagekräftig umbenennen (z.B. get_real() oder GetReal() ). Wenn diese Methoden eh nur Lesezugriff bieten, kannst du sie als const deklarieren. Es bräuchte auch noch ein Gegenstück, also so etwas wie set_real() , weil man ja eventuell nur einen Teil neu setzen will.

    Was die Logging-Ausgaben betrifft: Notfalls kannst du sie auch in der Klasse belassen und bedingt kompilieren. Wenn ein bestimmtes Makro definiert wurde, werden Ausgaben getätigt, sonst nicht. Das ist sehr praktisch für Debug-Ausgaben und wird normalerweise problemlos weggelassen.

    #ifdef DCOMPLEX_LOG
    std::cout << "Objekt erzeugt." << std::endl;
    #endif
    

    In der Hauptdatei könnte z.B. stehen:

    #define DCOMPLEX_LOG
    #include "dcomplex.h"
    

    Das einzige Problem ist vielleicht der Platz (3 Zeilen für eine Ausgabe). Das könnte man mit einem weiteren Makro lösen ;):

    #ifdef DCOMPLEX_LOG
     #define LOG_OUTPUT(a) std::cout << a << std::endl // Logging-Ausgabe
    #else
     #define LOG_OUTPUT(a) (void)0    // ansonsten macht das Makro nichts
    #endif
    

    Angewandt:

    LOG_OUTPUT("Objekt erzeugt.");
    

    Wie du siehst, sind solche Makros ziemlich praktisch. Sieh jedoch davon ab, sie zu häufig zu verwenden, sie haben nämlich beträchtliche Nachteile. Aber für Debug-Loggingausgaben sind sie meiner Ansicht nach durchaus legitim.



  • Wie du siehst, sind solche Makros ziemlich praktisch. Sieh jedoch davon ab, sie zu häufig zu verwenden, sie haben nämlich beträchtliche Nachteile. Aber für Debug-Loggingausgaben sind sie meiner Ansicht nach durchaus legitim.

    Phu..
    Ich würde da eher, wenn schon eine statische Logger Klasse schreiben, wo man den outputstream angeben kann. Dort kann man die Ausgabemenge auch noch mit Makros steuern, wenn man mag. Aber so direkt mit Makros rumfrickeln, würde ich abraten.

    Mal abgesehen von Tracing ist das aber ja eigentlich nicht nötig und gehört ansonsten auch nicht in die Klasse.



  • Das mit dem Logging is ja nur um mal zu sehen, dass er da nen Konstruktor aufruft. Is ne feine Sache mit den Markos, muss ich mir mal genauer angucken.
    Jedoch verstehe ich nicht warum häufiger ein Objekt der Klasse vernichtet als erzeugt wird. Das is doch unlogisch.



  • ntfs2008 schrieb:

    Jedoch verstehe ich nicht warum häufiger ein Objekt der Klasse vernichtet als erzeugt wird. Das is doch unlogisch.

    Du hast Kopierkonstruktor-Aufrufe auch nicht ins Logging miteinbezogen.



  • Ach leck mich fett! Da war ja was!
    Wenn ich also noch hinzufüge:

    dcomplex(const dcomplex &c){
    cout << "Objekt erzeugt.\n";}
    

    reicht das dann? oder muss ich im kopierkonstruktor noch definieren wie er zu kopieren hat?
    Muss eigentlich das c noch mit rein? (von dcomplex(const dcomplex &c){..)



  • habs gerade mal getestet.
    es funktioniert, vielen dank darauf wäre ich im leben nicht gekommen.
    übrigends das c kann man weglassen, aber das wusstet ihr bestimmt eh 😉
    und kopieren kann er auch alleine. ^^



  • Nein. Du musst die Member manuell kopieren, wenn du den Kopierkonstruktor selber implementierst. Genau so wie du bei der Definition einen Bezeichner für den Parameter angeben musst (wie in jeder Funktion).

    Deklaration in der Klasse:

    dcomplex(const dcomplex& other); // oder
    dcomplex(const dcomplex&); // würde ich aber generell nicht empfehlen
    

    Definition ausserhalb:

    dcomplex::dcomplex(const dcomplex& other)
    : re(other.re)
    , im(other.im)
    {
       std::cout << "Objekt erzeugt.\n";
    }
    

    Gewöhn dir am besten gleich an, immer std:: vor die Bezeichner der Standardbibliothek zu schreiben, dann kriegst du damit keine Probleme.



  • ohja stimmt, ich habe ganz andere werte erhalten.
    da hatte er wohl noch irgendwelche werte aus dem speicher genommen.
    leider ohne zu meckern. gut das du es gemacht hast ;-P
    okay ab jezt wird immer std:: davor geschrieben.
    außer im hauptprogramm. da nehme ich ja eh nur die std`s



  • dcomplex::dcomplex(double x){
        re = 0;
        im = x; // <-- WTF???
        cout << "\nObjekt erzeugt.\n";
    }
    

    EDIT:

    Genau so wie du bei der Definition einen Bezeichner für den Parameter angeben musst (wie in jeder Funktion).

    Nö, nur wenn man den/die Parameter auch verwenden will:

    int Func(boost::type<int>)
    {
        return 1;
    }
    double Func(boost::type<double>)
    {
        return 0.1;
    }
    


  • hustbaer schrieb:

    dcomplex::dcomplex(double x){
        re = 0;
        im = x; // <-- WTF???
        cout << "\nObjekt erzeugt.\n";
    }
    

    was stimmt daran nicht?



  • Hallo,

    ntfs2008 schrieb:

    hustbaer schrieb:

    dcomplex::dcomplex(double x){
        re = 0;
        im = x; // <-- WTF???
        cout << "\nObjekt erzeugt.\n";
    }
    

    was stimmt daran nicht?

    passt schon...

    MfG,

    Probe-Nutzer



  • Probe-Nutzer schrieb:

    Hallo,

    ntfs2008 schrieb:

    hustbaer schrieb:

    dcomplex::dcomplex(double x){
        re = 0;
        im = x; // <-- WTF???
        cout << "\nObjekt erzeugt.\n";
    }
    

    was stimmt daran nicht?

    passt schon...

    Nö! Woher soll der Anwender denn wissen, dass er den imaginärteil angibt, wenn er nur ein Parameter angibt? Dann mach wenigstens

    dcomplex::dcomplex (const double &_im)
    : re (0), im (_im)
    {}
    

    Wobei ich diesen CTor einfach weglassen würde...

    Übrigens würde ich davon abraten, die Variablenbez. in Headern wegzulassen, nur weil man nicht gezwungen wird, sie da hinzuschreiben... Im Header kann man viel schneller nachsehen, was für Variablen erwartet werden...

    bb



  • Ich würde Parameterbezeichner grundsätzlich immer angeben, auch bei der Deklaration. Es ist dann einfach übersichtlicher, und man weiss, was für Argumente übergeben werden müssen, und nicht nur, von welchem Typ sie sein müssen.

    hustbaer schrieb:

    Genau so wie du bei der Definition einen Bezeichner für den Parameter angeben musst (wie in jeder Funktion).

    Nö, nur wenn man den/die Parameter auch verwenden will

    Danke für die Berichtigung. Aber es wird sowieso in 99% der Fälle so sein, dass man die Argumente innerhalb der Funktion benötigt.



  • unskilled schrieb:

    Probe-Nutzer schrieb:

    Hallo,

    ntfs2008 schrieb:

    hustbaer schrieb:

    dcomplex::dcomplex(double x){
        re = 0;
        im = x; // <-- WTF???
        cout << "\nObjekt erzeugt.\n";
    }
    

    was stimmt daran nicht?

    passt schon...

    Nö! Woher soll der Anwender denn wissen, dass er den imaginärteil angibt, wenn er nur ein Parameter angibt?

    Hi also wenn man nur eine Zahl angibt dann ist es doch wohl klar das dies nur der imaginäre Teil ist. Wenns andersrum wäre dann bräuchte ich doch wohl kaum komplexe Zahlen. Es würden dann ja einfache reelle reichen.

    [quote="unskilled"]

    Probe-Nutzer schrieb:

    Dann mach wenigstens

    dcomplex::dcomplex (const double &_im)
    : re (0), im (_im)
    {}
    

    Wobei ich diesen CTor einfach weglassen würde...

    Was macht der obrige Code? Da kann ich absolut nichts mit anfangen.
    Zudem: Was ist ein CTOR? Konstruktor oder wie?

    [quote="unskilled"]

    Probe-Nutzer schrieb:

    Übrigens würde ich davon abraten, die Variablenbez. in Headern wegzulassen, nur weil man nicht gezwungen wird, sie da hinzuschreiben... Im Header kann man viel schneller nachsehen, was für Variablen erwartet werden...

    bb

    Klingt absolut logisch! Wird ab jetzt beherzigt! Danke
    Gruß Timo



  • ntfs2008 schrieb:

    ...
    Hi also wenn man nur eine Zahl angibt dann ist es doch wohl klar das dies nur der imaginäre Teil ist....

    Also mir begegnet dieser Schluss zum ersten Mal.
    Da habe ich sogar den gegenteiligen Schluss ("Wenn nur ein Argument, dann ist doch klar, dass es nur der Realteil ist!") sowohl öfter begegnet als auch ein wenig schlüssiger (was nicht bedeuten soll, dass ich das so machen würde!).

    Was würdest Du denn erwarten bei:

    double real = 1.2;
       dcomplex compl;
    
       compl = real;
    

    Würdest Du da auch erwarten, dass compl rein imaginär wäre ?
    OK, den operator=(double) musst Du nicht (so) implementieren, aber semantisch ist er seeeehr nah am Ctor(double), z.B: in:

    void do_with_complex(dcomplex d);
    
    int main() {
       double real = 1.2;
       do_with_complex(real);
    ...
    

    unskilled schrieb:

    Probe-Nutzer schrieb:

    Hallo,

    ntfs2008 schrieb:

    hustbaer schrieb:

    dcomplex::dcomplex(double x){
        re = 0;
        im = x; // <-- WTF???
        cout << "\nObjekt erzeugt.\n";
    }
    

    was stimmt daran nicht?

    passt schon...

    Nö! Woher soll der Anwender denn wissen, dass er den imaginärteil angibt, wenn er nur ein Parameter angibt? ...

    sowas macht z.B. folgendermaßen Probleme:

    dcomplex a1(1,2), a2(3.4), a3(5,6);
    

    Na ? War das wirklich das, was der Programmierer wollte ?
    Insgesamt muss man sehr vorsichtig sein mit solchen "Konvertierungstoren" (Ctor wenigstens "explicit" machen)...

    Gruß,

    Simon2.



  • Wenn man die Parameterbezeichner richtig benennt (und "x" ist meines Erachtens kein aussagekräftiger Bezeichner), kann man auch folgendes tun:

    dcomplex::dcomplex(double real = 0, double imag = 0)
    : re(real)
    , im(imag)
    {
    }
    

    Dann hat man auch gleich für einen Default-Ctor gesorgt. Den Zuweisungsoperator müsste man in diesem Fall angleichen, um die gleiche Semantik für 1 Argument zu erzielen.

    Es ist wohl naheliegender, den Realteil zu spezifizieren, wenn man nur ein Argument übergibt.

    dcomplex c = 4; // rechts steht der Wert 4, wieso sollte
                    // links danach nicht auch den selben Wert haben?
    

    Oder, um ganz sicher zu sein, kann man es wie Simon2 machen: Konstruktor explizit und ohne Defaultparameter (man muss so auch ausdrücklich angeben, dass der Imaginärteil 0 ist, wenn man eine rein reelle komplexe Zahl will).



  • Nexus schrieb:

    ...
    Es ist wohl naheliegender, den Realteil zu spezifizieren, wenn man nur ein Argument übergibt...

    😃

    @ntfs: siehste ? :p 😉

    Gruß,

    Simon2.



  • unskilled schrieb:

    Nö! Woher soll der Anwender denn wissen, dass er den imaginärteil angibt, wenn er nur ein Parameter angibt?

    Und woher soll ntfs2008 wissen, dass das gemeint ist?

    ntfs2008 hatte eventuell andere Bedenken, als er sich wunderte, warum der Kommentar da steht.

    Da, wo der Kommentar steht, gilt also "passt schon", nichts anderes war gemeint, wenn auch noch mehr dazu zu sagen gewesen wäre:

    Passendere Anmerkung wäre also gewesen: warum nur ein solcher Konstruktor, warum dann nicht auch einer, der nur den Realteil initialisiert, warum überhaupt dieser Konstruktor, oder...

    Ich dachte mir auch, dass dies der Grund ist, aber dann bitte doch explizit und nicht implizit, so hilft das auch nicht so versierten Leuten.

    MfG,

    Probe-Nutzer



  • Probe-Nutzer schrieb:

    ...
    Und woher soll ntfs2008 wissen, dass das gemeint ist?...

    Da verteigst Du wohl löblich und vehement eine leere Schachtel:

    ntfs2008 schrieb:

    ...also wenn man nur eine Zahl angibt dann ist es doch wohl klar das dies nur der imaginäre Teil ist....

    Es war wohl tatsächlich eine bewusste Designentscheidung.

    Gruß,

    Simon2.



  • Simon2 schrieb:

    ntfs2008 schrieb:

    ...
    Hi also wenn man nur eine Zahl angibt dann ist es doch wohl klar das dies nur der imaginäre Teil ist....

    Also mir begegnet dieser Schluss zum ersten Mal.
    Da habe ich sogar den gegenteiligen Schluss ("Wenn nur ein Argument, dann ist doch klar, dass es nur der Realteil ist!") sowohl öfter begegnet als auch ein wenig schlüssiger (was nicht bedeuten soll, dass ich das so machen würde!).

    Was würdest Du denn erwarten bei:

    double real = 1.2;
       dcomplex compl;
    
       compl = real;
    

    Wie soll das gehen? compl hat doch den Datentyp dcomplex und real den Datentyp double. Da muss man doch zunächst den operator = überladen oder wie ist das?

    Also man muss zunächst einmal bedenken wofür wir die komplexe Rechnung überhaupt benötigen. Warum sollte ich eine reelle Zahl als komplexe Darstellen wollen? Das ist doch absolut unlogisch und auch irgendwie nicht naturgemäß.
    Ein wichtiges Anwendungsgebiet ist hier die Elektrotechnik. Widerstände sind hier rein reell, Spulen und Kondensatoren sind rein imaginär. Da liegt es doch auf der Hand Resistanzen als double oder float zu deklarieren und Impedanzen und Admitanzen als komplexe. Die Summe daraus ergibt logischerweise natürlich wieder eine komplexe Zahl. Aber wie und was gerechnet werden soll dass muss ich doch in meinen Methoden und Überladungen beschreiben.
    Also ich habe mich ein komplettes Semester mit der komplexen Rechnung in allen erdenklichen Variationen rumschlagen müssen und bin mir sicher, dass dies auch im Programm richtig umgesetzt wurde.
    Es gibt einfach nur rein reelle (double), rein imaginäre (dcomplex) und komplexe (dkomplex) Objekte.

    Simon2 schrieb:

    sowas macht z.B. folgendermaßen Probleme:

    dcomplex a1(1,2), a2(3.4), a3(5,6);
    

    Na ? War das wirklich das, was der Programmierer wollte ?
    Insgesamt muss man sehr vorsichtig sein mit solchen "Konvertierungstoren" (Ctor wenigstens "explicit" machen)...

    Gruß,

    Simon2.

    Ne ganz bestimmt nicht. Aber das selbe Problem hast du doch auch, wenn du den Wert dem Realteil zuweist...


Anmelden zum Antworten