Frage zur Vererbung


  • Mod

    Ich glaube, du interpretierst auch das Disassembly falsch. Da wird gar nichts neu erstellt. Der Wert von var in dem aufrufenden Objekt (hier h) wird überschrieben. Ganz sicher. Siehst du doch, wenn du h.var hinterher ausgibst.



  • Ok mit dem "Variable wird neu erstellt" das war wohl der falsche ausdruck.

    void hen :: init ()
    
    0040158E  /$ 55             PUSH EBP
    0040158F  |. 89E5           MOV EBP,ESP
    00401591  |. 8B45 08        MOV EAX,DWORD PTR SS:[EBP+8]
    00401594  |. C700 2C010000  MOV DWORD PTR DS:[EAX],12C    ; 12c = 300
    0040159A  |. 5D             POP EBP
    0040159B  \. C3             RETN
    

    Nachdem ich hen::init() ausgeführt habe steht in EAX die Adresse: 00443024
    An EAX(00443024) steht auch die 300 (12c hex).

    Wenn dann meine Funktion void data :: print () augerufen wird ist das hier die Stelle die mich jetzt wundert:

    Ein Teil aus der Funktion void data :: print ()
    00401566 |. 8B45 08  MOV EAX,DWORD PTR SS:[EBP+8] ; EBP+8 = 00443020
    

    Würde EBP+8 auf die Adresse 00443024 zeigen dann würde ich mit der Funktion void data :: print () ja auch die 300 ausgeben bekommen.

    Ich dachte eigentlich über die Vererbung kann man auf die Variablen einer anderen Klasse zugreifen.

    Was mache ich da falsch oder wo ist da der Denkfehler?



  • Ok so kann ich es noch besser Erklären was mich wundert.

    void data :: print ()
     {
         std::cout<<"data::print() " << &var << std::endl; // Gibt aus 0x443020
     }
    
    void hen :: init ()
     {
         std::cout<<"hen::init() " << &var << std::endl; // Gibt aus: 0x443024
     }
    

    Hier habe ich jetzt erwartet das ich zweimal die selbe Adresse herausbekomme.


  • Mod

    Das was du da heraus bekommst, hängt doch von der Objektinstanz ab, sofern var nicht static ist. Ich glaube, du hast das ganze Grundkonzept von Klassen und Objekten nicht verstanden. Ein Objekt ist die konkrete Instanzierung einer Klasse. Davon kann es ganz viele geben.



  • Dann hab ich hier doch jetzt 2 int Variablen im Speicher stehen oder nicht?
    Weil die 8 und die 3 stehen ja jeweils an verschiedenen Stellen im Speicher.

    #include <iostream>
    using namespace std;
    
    class car
    {
        public:
            int sp;
    
    };
    
    car a;
    car b;
    
    int main()
    {
    a.sp = 8;
    cout<<a.sp<<endl; // Gibt 8 aus
    
    b.sp = 3;
    cout<<b.sp<<endl; // Gibt 3 aus
    
    system("PAUSE");
    return 0;
    }
    

    Ich habe mir hier: http://www.cpp-tutor.de/cpp/le09/klassen.html
    auch mal den Teil "Objekte in der Theorie" durchgelesen aber das hilft mir auch nicht weiter.

    Hat jemand zufällig noch eine bessere Quelle zu dem Thema?


  • Mod

    Die Grundlagen der Objektorientierung in C++ entnimmst du lieber einem guten Buch. Wenn dir nämlich die Sache mit den Instanzierungen unklar (beziehungsweise seit heute neu) ist, dann hast du es irgendwie geschafft, die Sprachmittel zwar zu kennen, aber nicht richtig verstanden zu haben. Und das ist nicht gut (für dich). Siehe den Link in meiner Signatur für Buchempfehlungen.



  • Nochmal kurz zu der Sache "erstellt im Speicher eine neue Variable".

    Das konnte ich im C++ Primer im Bezug zu Objekten finden:

    In C++, an object is just a variable, and the purest definition is “a
    region of storage” (this is a more specific way of saying, “an object
    must have a unique identifier,” which in the case of C++ is a
    unique memory address). It’s a place where you can store data, and
    it’s implied that there are also operations that can be performed on
    this data.

    Ich habe das so verstanden:
    - Jedes Objekt hat eine einzartige Adresse.
    - Ein Objekt ist eine bestimmte Region im Speicher.

    Im Disassembly von IDA Pro sieht das jetzt so aus:

    .text:00401168 var_8 = dword ptr -8
    .text:00401168 var_4 = dword ptr -4

    Und das sind doch 2 Variablen die da auf dem Stack angelegt werden ... (Bei Funktionen da konnte ich dieses Verhalten nicht beobachte, die Funktion wurden nicht 2 mal angelegt.)

    Bei Funktionen sah es so aus:

    lea eax,dword ptr ds:[ebp-4]
    push eax
    call Adresse ABC

    lea edx,dword ptr ds:[ebp-8]
    push edx
    call Adresse ABC

    Das Program funktionierte natürlich selbst wenn man lea und push vor den 2 Funktionsaufrufen entfernt hat.

    Ausgabe von IDA Pro:

    .text:00401168 ; int __cdecl main(int argc,const char **argv,const char *envp)
    .text:00401168 _main           proc near               ; DATA XREF: .data:0040204Co
    .text:00401168
    .text:00401168 var_8           = dword ptr -8
    .text:00401168 var_4           = dword ptr -4
    .text:00401168 argc            = dword ptr  8
    .text:00401168 argv            = dword ptr  0Ch
    .text:00401168 envp            = dword ptr  10h
    .text:00401168
    .text:00401168                 push    ebp
    .text:00401169                 mov     ebp, esp
    .text:0040116B                 add     esp, 0FFFFFFF8h
    .text:0040116E                 push    offset s        ; "ok"
    .text:00401173                 push    ds:@_STL@cout   ; int
    .text:00401179                 call    sub_401288
    .text:0040117E                 add     esp, 8
    .text:00401181                 mov     [ebp+var_4], 1
    .text:00401188                 mov     [ebp+var_8], 2
    .text:0040118F                 push    offset sub_401680
    .text:00401194                 lea     eax, [ebp+var_4]
    .text:00401197                 push    eax
    .text:00401198                 push    offset aC       ; "c: "
    .text:0040119D                 push    ds:@_STL@cout   ; int
    .text:004011A3                 call    sub_401288
    .text:004011A8                 add     esp, 8
    .text:004011AB                 push    eax
    .text:004011AC                 call    @$beql$qrx5_GUIDt1 ; operator==(_GUID &,_GUID &)
    .text:004011B1                 add     esp, 8
    .text:004011B4                 push    eax
    .text:004011B5                 call    sub_401268
    .text:004011BA                 add     esp, 8
    .text:004011BD                 push    offset sub_401680
    .text:004011C2                 push    [ebp+var_4]
    .text:004011C5                 push    offset aC_v     ; "c.v: "
    .text:004011CA                 push    ds:@_STL@cout   ; int
    .text:004011D0                 call    sub_401288
    .text:004011D5                 add     esp, 8
    .text:004011D8                 push    eax
    .text:004011D9                 call    @$beql$qrx5_GUIDt1_0 ; operator==(_GUID &,_GUID &)
    .text:004011DE                 add     esp, 8
    .text:004011E1                 push    eax
    .text:004011E2                 call    sub_401268
    .text:004011E7                 add     esp, 8
    .text:004011EA                 push    offset sub_401680
    .text:004011EF                 lea     edx, [ebp+var_8]
    .text:004011F2                 push    edx
    .text:004011F3                 push    offset aB       ; "b: "
    .text:004011F8                 push    ds:@_STL@cout   ; int
    .text:004011FE                 call    sub_401288
    .text:00401203                 add     esp, 8
    .text:00401206                 push    eax
    .text:00401207                 call    @$beql$qrx5_GUIDt1 ; operator==(_GUID &,_GUID &)
    .text:0040120C                 add     esp, 8
    .text:0040120F                 push    eax
    .text:00401210                 call    sub_401268
    .text:00401215                 add     esp, 8
    .text:00401218                 push    offset aOk2     ; "ok2"
    .text:0040121D                 push    ds:@_STL@cout   ; int
    .text:00401223                 call    sub_401288
    .text:00401228                 add     esp, 8
    .text:0040122B                 mov     [ebp+var_4], 8
    .text:00401232                 push    [ebp+var_4]
    .text:00401235                 push    ds:@_STL@cout   ; _STL::cout
    .text:0040123B                 call    @$beql$qrx5_GUIDt1_0 ; operator==(_GUID &,_GUID &)
    .text:00401240                 add     esp, 8
    .text:00401243                 push    offset command  ; "PAUSE"
    .text:00401248                 call    _system
    .text:0040124D                 pop     ecx
    .text:0040124E                 xor     eax, eax
    .text:00401250                 pop     ecx
    .text:00401251                 pop     ecx
    .text:00401252                 pop     ebp
    .text:00401253                 retn
    .text:00401253 _main           endp
    

    Das ganze nochmal in C++

    #include <iostream>
    using namespace std;
    
    class var
    {
        public:
            int v;
    };
    
    int main()
    {
    var c;
    var b;
    cout<<"ok";
    
    c.v=1;
    b.v=2;
    
    cout<<"c: "  << &c<<endl;
    
    cout<<"c.v: " << c .v<<endl;
    
    cout<<"b: " <<&b<<endl;
    cout<<"ok2"; 
    c.v=8;
    
    cout<<c.v;
    
    system("PAUSE");
    return 0;
    }
    

    Daher werden meiner Meinung nach durch die Befehle:

    c.v=1;
    b.v=2;

    Zwei Variablen auf dem Stack angelegt, obwohl man ja oben in der Klasse nur eine deklariert hat aber man hat ja die zwei Objekte und diese "werden zu" Variable var. (Aber ein Objekt ist ja auch eine Variable).



  • Du hast mit

    var c;
    var b;
    

    zwei Variablen auf dem Stack angelegt. Mit

    c.v=1;
    b.v=2;
    

    werden diese beiden benutzt.

    class var
    {
        public:
            int v;
    };
    

    definiert keine Variable, sondern eine Klasse - deswegen auch "class".



  • srsbsns schrieb:

    Nochmal kurz zu der Sache "erstellt im Speicher eine neue Variable".

    Vergiss die Diskussion. Solange du nicht den Unterschied zwischen einer Klasse und einen Objekt (Instanz) verstehst, bringt die Diskussion nichts.

    Eine Klasse ist nur eine Schablone, sie existiert nicht im Speicher. Ein Objekt ist eine konkrete Instanz die diese Schablone erfüllt.

    class Auto
    {
      public:
        Auto(
          string const & marke)
        : marke(marke)
        {
        }
    
        string marke;
    }
    
    int main()
    {
      Auto auto1("Audi"), auto2("Ford"); // <-- Jedes Auto hat seinen eigenen Wert für marke
    }
    

    Ob man Vererbung verwendet oder nicht ist dabei völlig egal, eine Instanz hat seine eigenen Variablenwerte.

    Wenn man wirklich (was eher selten ist) eine Variable haben will die für alle Instanzen die gleiche ist, muss man diese mit static markieren, dann gibt es wirklich nur eine.

    P.S: Wenn man eine Klasse Mensch mit einem Namen hat, erwartet man ja auch nicht das jeder Egon Müller heißt... ;p



  • Ich glaub so langsam komm ich dahinter. 🙂
    Danke für euere Geduld. 😃


Anmelden zum Antworten