Objektorientierte Programmierung in C++

  • Hallo, ich hoffe ihr könnt mir weiterhelfen. Ich programmiere normalerweise C#, manchmal auch Java und habe noch nie etwas in C++ gemacht, in C auch nur was für die Uni nötig war (Sysprog, Übersetzerbau), also bitte um Nachsicht.

    Ich beginne gerade ein kleines Programm in C++ und wüsste gerne, ob ich einige Dinge richtig verstanden habe und was da sozusagen Best Practise ist.

    Neues Objekt erzeugen
    Hier wird der Speicher automatisch freigegeben, wenn der Scope verlassen wird?

    Code
    ClassA objA;


    Auf Methoden greift man hier mit . zu?

    Code
    objA.doSomething();

    Hier wird der Speicher nicht automatisch freigegeben, sondern mit delete?

    Code
    ClassA *objA = new ClassA();


    Auf Methoden greift man hier mit -> zu?

    Code
    objA->doSomething();

    Welche der beiden Möglichkeiten verwendet man üblicherweise?

    Und wie sieht das aus, wenn ich in einer Klasse als Eigenschaft ein Objekt einer anderen Klasse habe, also in Java etwa so:

    Code
    class TestA
    {
       private TestB testB;
    
       public TestB getTestB() { return testB; }
    
    
       public void setTestB(TestB val) { testB = val; }
    }


    Wie würde man diese Klasse in C++ schreiben?
    So (vereinfacht: Headerfile und so):

    Code
    class TestA
    {
       private TestB testB;
    
       public TestB getTestB() { return testB; }
    
    
       public void setTestB(TestB val) { testB = val; }
    }


    oder so:

    Code
    class TestA
    {
       private TestB *testB = new TestB();
    
       public TestB* getTestB() { return testB; }
    
    
       public void setTestB(TestB *val) { testB = val; }
    }

    Übergabe von Objekten als Parameter an Funktionen

    Wenn ich das richtig verstanden habe, wird hier eine Kopie des Objekts übergeben?

    Code
    public int doSomething(ClassA objA) { ... }
    ClassA objA;
    doSomething(objA);

    Und hier wird eine Referenz auf das Objekt übergeben?

    Code
    public int doSomething(ClassA *objA) { ... }
    ClassA *objA = new ClassA();
    doSomething(objA);

    Welche von beiden Möglichkeiten verwendet man "im Normalfall"?

    Einmal editiert, zuletzt von xruxl (24. August 2011 um 21:48)

  • Zuerst mal zu der Rückgabe-Thematik:
    http://www.mistybeach.com/articles/WhyID…geProjects.html
    Hat glaub ich hier mal jemand geposted (oder woanders?).
    Hier wird die Problematik erklärt ;).

    Auch zu empfehlen sind "Effective C++" und gibt diverse Seiten im Internet zu den typischen Stolperfallen in C++ (und ich schnappe nach Jahren auch oft noch welche auf, besonders wenn ich zwischenzeitlich Java o.ä. programmiere)


    Hier wird der Speicher automatisch freigegeben, wenn der Scope verlassen wird?


    Ja.


    Auf Methoden greift man hier mit . zu?


    Ja.


    Hier wird der Speicher nicht automatisch freigegeben, sondern mit delete?


    Ja.


    Auf Methoden greift man hier mit -> zu?


    Ja.


    Welche der beiden Möglichkeiten verwendet man üblicherweise?


    Kommt auf den Anwendungsfall an.
    In moderneren Programmen (als embedded systems typ hab ich sowas leider meistens nicht) verwendet man statt der manuellen new-delete Variante üblicherweise Smart-Pointer (siehe zb. boost), die sich selbst um den Speicher kümmern. Einen simplen Reference Counting Pointer kann man sich auch relativ leicht selber schreiben (glaub hab noch irgendwo eine Implementierung von mir rumgammeln wenns dich interessiert, aber findet man sicher auch haufenweise im Internet).

    Insofern sollte man auch immer die Destruktor Thematik beachten.
    Auf der positiven Seite ist die Kontrolle über die Speicherfreigabe auch ein nützliches Tool. Klassisches Beispiel Semaphor (boost: scoped_lock) der beim Anlegen automatisch gelockt wird und bei Ende des Blocks wieder freigegeben (grad wenn man exceptions oder so verwendet..)


    Und wie sieht das aus, wenn ich in einer Klasse als Eigenschaft ein Objekt einer anderen Klasse habe, also in Java etwa so:


    Siehe den Link am Anfang.
    Aber kommt auch auf den Anwendungsfall an..
    Du kannst auch eine Referenz zurückgeben:
    ClassA a;
    ClassA& methodA( ) { return a; }

    Oder zb. einen Pointer so:
    ClassA* methodA() { return &a; }

    Grad beim Pointer muss man sich halt auch immer überlegen, was der "draussere" damit anfängt.. bzw. dieser muss wissen, ob er das Ding freigeben muss oder nicht (könnte ja auch ein return new ClassA(); in der Methode sein).

    Die erste Variante die du geposted hast liefert übrigens eine Kopie (siehe dazu Copy Constructor und operator=... *seufz*).
    Bei gewissen Klassen (zb. kleinen Data-Holdern) auch eine sichere Alternative.


    Übergabe von Objekten als Parameter an Funktionen

    Welche von beiden Möglichkeiten verwendet man "im Normalfall"?

    Bei zweiterem wird ein Pointer übergeben.
    Eine Referenz würdest so übergeben:

    Code
    public int doSomething(ClassA& objA) { ... }
    ClassA objA;
    doSomething(objA);

    Und wieder sind im Prinzip alle Möglichkeiten offen (inklusive smart-pointer).
    Kleine Datenobjekte wie strings etc. kannst kopieren, dann kann damit kein blödsinn getrieben werden.
    Wenn das Objekt verändert werden soll, musst auf jeden Fall Referenz, Smart-Pointer oder normalen Pointer verwenden.

    Naja, in Summe:
    Ich hab C++ früher geliebt.
    Inzwischen gehts mir nurmehr auf die Nerven, mir jedesmal überlegen zu müssen, was die optimale Variante ist und wie man sich am wenigstens das Hackl ins Kreuz haut. Insofern verstehe ich nciht, was Leute an Java als schwer empfinden - da kann man doch eigentlich praktisch nix falsch machen.
    C++ mag anfangs auch leicht wirken, wenn man aber mal tief eintaucht, findet man dann doch ziemlich viele Fallstricke auf die man aufpassen sollte. Und viele Dinge, die man bedenken sollte (was packe ich in Header, was in Cpp, was forward deklarieren, wo forward deklarieren, in welcher Reihenfolge inkludiere ich etc.). Alles Dinge, die einem in Java einfach wurscht sein können.

    Achja:
    Ich kenne Libraries, die verwenden praktisch überall nurmehr einen smart-pointer bis auf gewisse Data-Holder, die kopiert werden.
    Vermutlich keine schlechte Lösung für die meisten Anwendungen.

    Hoffe hab jetzt in der Eile keinen Blödsinn getippt, man möge mich korrigieren :)

    Einmal editiert, zuletzt von mtoman (24. August 2011 um 23:14)


  • Auf Methoden greift man hier mit . zu?

    Code
    objA.doSomething();

    Auf Methoden greift man hier mit -> zu?

    Code
    objA->doSomething();

    obj->abc() ist die abkürzung für (*obj).abc()

    Und statischer Speicher wird autmatisch freigegeben, dynamischer nicht (statisch: Class abc;, dynamisch: Class *abc = new Class())
    Smartpointer werden statisch allokiert (somit auch autom. freigegeben) und können dadurch das dynamisch allokierte freigeben

    bzgl parameter-übergabe:
    Java: void function(Object a, int b)
    C++: void Class::function(Object &a, int b)

    noch als tipp: http://www.parashift.com/c++-faq/

    Thomas

  • Hat sich erledigt.



    Zu dem mittlerweilende fehlenden Thema sei noch angemerkt: Wenn du mit msvc++ programmierst und du mögchtest das es auch mit dem gcc auf linux kompiliert solltest du die reihenfolge der initialisierungen im konstruktor der reihenfolge der membervariablen anpassen, heisst:

    Code
    private:
    int a;
    float b;


    wird auch mit 1) initialisiert, bei 2) meckert der gcc

    Code
    1) Klasse::Klasse() : a(0), b(1.0f) {}
    2) Klasse::Klasse() : b(1.0f), a(0) {}

    und beim ableiten würd ich auch die elternklasse explizit noch anschreiben

    Thomas

  • Zu dem mittlerweilende fehlenden Thema sei noch angemerkt: Wenn du mit msvc++ programmierst und du mögchtest das es auch mit dem gcc auf linux kompiliert solltest du die reihenfolge der initialisierungen im konstruktor der reihenfolge der membervariablen anpassen



    Puh, das wusste ich auch nicht. Noch so eine kleine Nervigkeit mehr ;)

  • Ich hab auch nur gelesen, dass man das machen soll. Aber trägt sicher zur Übersichtlichkeit bei.



    Stimmt natürlich, mach ich prinzipiell eh auch eher so. Aber denk mir, wenn man im nachhinein umsortiert oder was dazwischen einfügt und dann mit netten Fehlermeldungen deswegen konfrontiert wird.. :)

    Achja, klassischer Fehler der mir gern passiert wenn ich ne Zeit Java intus habe:
    class A : public B
    das public vergessen. Die Fehlermeldungen fand ich dann auch nie so mörderisch intuitiv beim MS Compiler.
    Oder halt Strichpunkt nachm } am Klassenende, aber das findet man dann relativ leicht.
    Virtual vergessen kann sich mit relativ eigenartigem Verhalten äussern.

    lg

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!