Fragen speziell an C++



  • Hi,

    ich will mir mal die Meinung von ein paar Profis zu meinen Fragen und Beispielen holen 🙂

    1. Deklaration okay oder total unsauber? (wenn unsauber, bitte sagen was igit ist.)

    class CLog 
    { 
    public: 
        CLog    (const char* FileName); 
    
        CLog& operator<< (const char* s); 
        CLog& operator<< (char* s); 
        // [...]  noch so 10. andere << operatoren
        CLog& operator<< (void* n); 
    
    private: 
        std::ofstream   m_Stream; 
        std::string     m_FileName; 
    };
    

    2. Welcher Konstruktor für CLog ist besser (richtiger) und wieso?

    CLog::CLog( const char* FileName ) : m_Stream(FileName), m_FileName(FileName) 
    { 
        this->m_Stream.close(); 
    }
    

    oder:

    CLog::CLog( const char* FileName )
    { 
        this->m_Stream(FileName);
        this->m_FileName(FileName);
        this->m_Stream.close(); 
    }
    

    4. ein operator << (richtig oder falsch? kann man was verbessern?)

    CLog& CLog::operator<< (const char* s) 
    {    
        this->m_Stream.open(this->m_FileName.c_str(), std::ios::out | std::ios::app); 
        this->m_Stream << s; 
        this->m_Stream.close(); 
    
        return *this; 
    }
    

    5. Ich benutze oft this-> um auf Private Member der Klasse zuzugreifen (siehe oben), gut oder schlecht? warum und wieso?

    6. Im Buch "Effektiv C++ Programmieren von Scott Meyers" steht man soll #defines vermeiden, sollte man dann sowas hier immer durch "const dateityp" ersetzen? z.B.: so?

    #define ENEMY_RAGE      0
    #define ENEMY_DRAGON    1
    #define ENEMY_FLYER     2
    

    durch

    const int ENEMY_RAGE    =   0
    const int ENEMY_DRAGON  =   1
    const int ENEMY_FLYER   =   2
    

    7. Müssen #define Makros für gutes C++ durch templates ersetzt werden? bzw. #define für alles durch anderes ersetzen und nur noch für include-guards benutzen?

    8. Das open-closed Principle besagt ja: Keine Globalen Variablen.
    Ist also extern verboten?

    // main.cpp:
    bool mainLoop = true;
    
    // menu.cpp
    extern mainLoop;
    

    9. Ist die Ungarische Notation für C++ noch angebracht oder sollte man sie nur noch in C benutzen und in C++ etwas anderes, wenn ja was und wieso, wenn nein wieso

    10. std::string überall dort benutzen wo sonst char* und char[] war? Oder sollte man gelegentlich auch noch char benutzen? wenn ja wo und wieso, wenn nein wieso?

    nun mehr ist mir grade net eingefallen 😉

    [edit]
    Frage 9 und 10 hinzugefügt

    [ Dieser Beitrag wurde am 26.03.2003 um 08:43 Uhr von Ikari editiert. ]

    [ Dieser Beitrag wurde am 26.03.2003 um 08:46 Uhr von Ikari editiert. ]



  • 1. naja, dir fehlen CCtor und COp Funktionen, per default führt der Compiler so etwas wie memcpy aus, wenn das okay ist für deine Klasse, ist das natürlich kein Problem, aber ich würde mich darauf nur verlassen, wenn ich PODs benutze.

    http://fara.cs.uni-potsdam.de/~kaufmann/?page=GenCppFaqs&faq=BigThree#Answ

    Dann ist der Name der Klasse schlecht gewählt

    (FAQ - Rund ...) Stilfrage: C als Präfix für Klassen

    2. natürlich das erstere

    http://fara.cs.uni-potsdam.de/~kaufmann/?page=GenCppFaqs&faq=Init#Answ

    3. <fehlt>

    4. Dein Weg ist ziemlich langsam erst eine Datei zu öffnen und diese dann zu schließen, generell versteh ich eh nicht den Sinn deiner CLog Klasse, da du ja nur ein ofstream kapselst (nur das ziemlich langsam)

    5. Naja, ich finde das ziemlich sinnlos, es bringt nicht mehr oder ist übersichtlicher IMHO, außerdem könnte die Gefahr bestehen, dass dein Compiler langsameren Code erzeugt, mach wenn dann so etwas CLog::variable=a;

    6. ja, du kannst aber natürlich auch enums nehmen

    7. #define benutzt man nur noch bei include-Guards, den Rest ersetzt man (inline-Funktionen, const-Variablen, templates)

    8. schau dir mal an, was extern bedeutet

    9. Das gibt eh nur wieder einen Flamewar: Aber folgendes ist meine Meinung
    Ungarische Notation braucht kein Mensch, weder in C noch in C++!

    10. char* kann man ja nur teilweise mit std::string vergleichen. Außerdem kann man IMHO nicht global sagen, das eine ist gut, das andere ist schlecht. Da muss man in jeder Situation schauen



  • HI,

    danke für die Infos!

    Also zu 1.: Die Klasse sollte eigentlich später Singleton werden und da braucht man meines wissens kein CCot 🙂 Hm also C als Präfix is nix? was würdest du denn nehmen? Ich fand bisher C als Präfix immer sehr Praktisch, nebenbei ist es sehr gängig 🙂

    zu 5: ist CLog::variable=a; und this->variable=a; nicht das selbe? 😕



  • Auch n Singleton hat nen Ctor (wie willst du ihn sonst instanziieren?)
    C als Prefix ist schlecht, weil es sinnlos ist. da gabs schon oft diskussionen drüber. aber C ist Böse, weil es sich mit der MFC beisst.

    die MFC verwendet C sozusagen als namespace ersatz
    CLog::variable=a; und this->variable=a; sind NICHT das selbe.

    :: spricht einen scope an
    und
    -> einen Member

    this->x schreibt man in C++ nicht. (nur wenn man es muss)



  • ich will mir mal die Meinung von ein paar Profis zu meinen Fragen und Beispielen holen

    Ich antworte einfach mal, obwohl ich die Definition von Profi nicht erfülle.

    1. Deklaration okay oder total unsauber?

    Keine Ahnung. Ich sehe hier nur ne *Definition*. Nämlich die einer Klasse.

    Das open-closed Principle besagt ja: Keine Globalen Variablen.

    Nein sagt es nicht. Das open-closed Principle sagt, dass eine Klasse, eine Funktion, ein Modul usw. offen gegenüber Erweiterungen und geschlossen gegenüber Veränderungen sein soll.
    Richtig ist, dass glohale Variablen leicht zu einer Verletzung dieses Prinzips führen können. Das OCP verbietet aber keinesfalls generell den Einsatz von globalen Variablen.

    Und noch ein Kommentar von mir: Lieber ne sinnvolle globale Variable, als ein überflüssiges Singleton.

    zu 5:

    Naja, ich finde das ziemlich sinnlos, es bringt nicht mehr oder ist übersichtlicher IMHO

    Das sehe ich genauso.

    außerdem könnte die Gefahr bestehen, dass dein Compiler langsameren Code erzeugt, mach wenn dann so etwas CLog::variable=a;

    Davon habe ich noch *nie* gehört. Letztlich bedeutet variable im Kontext von Log sowieso nichts weiter als this->CLog::variable.
    Die explizite Qualifizierung mit CLog:: führt allerdings dazu, dass keine dynamische Bindung mehr durchgeführt wird. Das spielt natürlich nur für virtuelle Methoden eine Rolle.

    Die Qualifizierung mit this-> hat in meinen Augen nur einen Vorteil. Man spart sich die Denkschwierigkeiten in Verbindung mit abhängigen Templatebasisklassen.

    Generell würde ich auf this-> aber auf jeden Fall verzichten.

    Müssen #define Makros für gutes C++ durch templates ersetzt werden? bzw. #define für alles durch anderes ersetzen und nur noch für include-guards benutzen?

    Also in der Regel sollte man sowenig wie Möglich von Makros Gebrauch machen. Einfach weil der Präprozessor mit seinen simplen Textersetzungsregeln nicht so toll in die Umgebung von C++ mit den komplizierten Typ- und Scope-Regeln passt.
    Auf der anderen Seite gibt es Dinge, die sich eigentlich nur mit Makros ellegant lösen lassen.
    Ich würde Makros also prinzipiell kritisch betrachten und, sofern ohne allzu großen Aufwand möglich, durch die enstprechenden C++ Sprachmittel ersetzen.

    9. Ist die Ungarische Notation für C++ noch angebracht

    Nein.

    std::string überall dort benutzen wo sonst char* und char[] war? Oder sollte man gelegentlich auch noch char benutzen? wenn ja wo und wieso, wenn nein wieso?

    std::string da benutzen, wo du die Dienste die diese Klasse anbietet brauchst.
    Wenn du nicht suchen/löschen/einfügen/.... musst, gibt es auch keinen Grund ein std::string-Objekt anzulegen.
    Ansonsten ist die Frage etwas unglücklich, da es tonnenweise Code gibt, wo du diese Frage gar nicht stellen kannst. Da wird char* verlangt und damit Ende.

    Also kurz: Benutze std::string wenn du kannst, char* wenn du musst. Auf jeden Fall solltest du char* und co verstehen und benutzen können. Das gilt natürlich auch für std::string.

    [ Dieser Beitrag wurde am 26.03.2003 um 11:49 Uhr von HumeSikkins editiert. ]



  • 1. Der Default-CCtor macht kein memcpy, sondern ruft für jedes Element rekursiv den CCtor auf. Da ofstream keinen hat, schlägt auch das Kopieren dieser Klasse fehl => man braucht keinen CCtor zu definieren. Das gleiche gilt für den Zuweisungsoperator.

    2. Ich würde

    CLog::CLog(const char * FileName): m_FileName(FileName) { }
    

    schreiben. Dann wird der Stream default-konstruiert und ist gleich geschlossen 🙂

    4. Anstatt die Datei jedesmal auf und zu zu machen, würde es vielleicht auch ein simples m_Stream << flush tun. Ach ja, und schreib nicht immer this-> davor. Hast du mal Python programmiert? Durch das m_ Präfix wird doch schon eindeutig ausgesagt, dass es sich um Member-Variablen handelt.

    @kingruedi: Ein Log sollte normalerweise gerade kritische Phasen (kurz vor einem Absturz) dokumentieren. Da wär es ziemlich sinnfrei, erst kilometerweise Text in irgendwelche Buffer zu schieben, das geht dann nämlich alles verloren. Je sicherer das direkt in der Datei landet, desto besser. U.U. würd ich das ganze auch in ein Mutex packen.



  • Der Default-CCtor macht kein memcpy

    Das gemeine ist, dass dieser Denkfehler in vielen (schlechten) Büchern kultiviert wird. Da steht dann immer sowas wie "der blabla führt eine bitweise Kopie durch". Herbert Schildt wird sich jetzt hoffentlich angesprochen fühlen 🙂



  • @Bashar

    Ein Log sollte normalerweise gerade kritische Phasen (kurz vor einem Absturz) dokumentieren. Da wär es ziemlich sinnfrei, erst kilometerweise Text in irgendwelche Buffer zu schieben

    jo, deswegen würde ich das mit std::flush lösen, wie du es auch vorgeschlagen hast

    Der Default-CCtor macht kein memcpy, sondern ruft für jedes Element rekursiv den CCtor auf.

    Oh, dass wusste ich gar nicht!


Anmelden zum Antworten