Warum ist ein Zeiger auf eine Methode 0x10 byte lang???



  • Hallo,

    ich verstehe den folgenden C++-Code nicht:

    #include <stdio.h>
    
    class Foo;
    
    class Bar
    {
    public:
      typedef void (Foo::*pFooFunc)();
      void RegisterCommand(pFooFunc pFoo) {}
    };
    
    // Die folgende Klassendefinition kann auch weggelassen werden
    class Foo
    {
      void FooFunc() {}
    };
    
    int _tmain()
    {
      int s = sizeof(Bar::pFooFunc);
      printf("%d", s);
      return 0;
    }
    

    Warum ist "sizeof(Bar::pFooFunc)" 0x10 ???
    Mach ich die "RegisterCommand" Methode raus, wird sie 0x4 (wie ich eigentlich erwartet habe)...

    Hat jemand eine Erklärung dafür?

    PS: Compiliert mit VS2003
    PPS: Comeau zeigt mir keine Fehler an...



  • Vor einer Weile kam schonmal das Thema "Funktionspointer" auf, da hatte N4C3R einen Link hierhin gepostet - eventuell hilft dir die dortige Beschreibung auch weiter 😉
    (Abschnitt "Member Function Pointers - why are they so complex?")



  • Jochen Kalmbach schrieb:

    #include <stdio.h>
    
    class Foo;
    
    class Bar
    {
    public:
      /// Die Deklaration von Foo ist noch nicht bekannt (nur ein Forward).
      /// Trotzdem sind Methodenzeiger auf Forward-deklarierte Klassen erlaubt.
      /// In diesem Falle wird virtuelle Vererbung + Mehrfachvererbung
      //// (komplexester Fall) angenommen.
      typedef void (Foo::*pFooFunc)();
      void RegisterCommand(pFooFunc pFoo) {}
    };
    
    // Die folgende Klassendefinition kann auch weggelassen werden
    class Foo
    {
      void FooFunc() {}
    };
    
    int _tmain()
    {
      int s = sizeof(Bar::pFooFunc);
      printf("%d", s);
      return 0;
    }
    

    Der Link, den CStoll schon wiederholt hat, ist natürlich auch sehr hilfreich. 🙂

    Edit: Warum die Größe des Zeigers auf 4 schrumpft, wenn du die Methode entfernst, ist mir aber auch nicht wirklich klar. Vielleicht irgendeine Optimierung/Eigenart des Compilers.



  • Das ganze ist ja noch schlimmer als gedacht...
    Wenn ich den Code in mehrere Dateien splitte (was ja der Normalfall ist), dann führt dies zu einem Speicherüberschreiber... (davon ist in dem Artikel bisher nichts zu lesen):

    foo.h:

    #pragma once
    class Foo
    {
    public:
      void FooFunc();
    };
    

    foo.cpp:

    #include "foo.h"
    
    void Foo::FooFunc() {}
    

    bar.h:

    #pragma once
    
    class Foo;
    class Bar
    {
    public:
      typedef void (Foo::*pFooFunc)();
      void RegisterCommand(pFooFunc pFoo);
    };
    

    bar.cpp:

    #include "bar.h"
    
    void Bar::RegisterCommand(pFooFunc pFoo) {}
    

    FooBar.cpp:

    #include "Bar.h"
    #include "Foo.h"
    
    int wmain()
    {
      Bar b;
      b.RegisterCommand(Foo::FooFunc);
    }
    

    Erzeugt bei mir:

    Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

    Da hier die verschiedenen CPP-Dateien von unterschiedlichen Größen des Methodenzeigers ausgehen...

    Ich verzweifle...



  • Dazu steht allerdings was in dem Artikel, da gibts neue Compiler-Schlüsselwörter und eine Compiler-Option die das verhindern.

    MfG SideWinder



  • SideWinder: Du bist Klasse!!! /vmg behebt das Problem (es geht einfach immer vom schlechtesten Fall aus)!


Anmelden zum Antworten