Der (Nichts-)Datentyp Void



  • In der WinAPI gibts z.B. die Funktion CreateThread, die einen Thread erzeugt und einen Funktionszeiger übergeben bekommt, dessen Funktion dann ausgeführt wird (als Thread).

    CreateThread hat jetzt noch die Möglichkeit, dieser Funktion die sie als Funktionszeiger übergeben bekommt ein gewissen Parameter beim Aufruf "als Thread" mitzugeben. Da man aber nicht weiß, welchen Typ dieser Parameter haben könnte (es soll ja allgemein sein) und es eine C API ist, benutzt man einen void Zeiger.

    Die Funktion muss dann ihren übergebenen void Zeiger in den entsprechenden Typ konventieren und hoffen, dass der Typ stimmt, ansonsten gibts einen netten Absturz.

    void threadFunktion(void* parameter) {
    int* data = (int*)parameter.
    }
    

    In C++ würde man allerdings den Cast durch einen reinterpret_cast oder static_cast ersetzten.

    int* data = reinterpret_cast<int*>(paramter);
    

    In C++ würde man das ganze Problem aber sowieso mit Templates bei CreateThread anstatt void Zeigern lösen, so dass man dann bequem und typsicher schreiben kann:

    void threadFunktion(int data) {
    }
    

    In C++ benutzt man eigentlich fast nur dann void Zeiger, wenn man gerade auf C API's zugreift, bzw. diese wrappt.

    Falls du das nicht verstehst, schlage CreateThread / wrapper bei Google nach.

    Die Motivation bei C++ ist eben weg von dem alten C zu kommen und ein bisschen sauberer zu coden, wie eben in Java. Aber reden wir nicht von Java ...



  • Skym0sh0 schrieb:

    so ist das doch oder?
    (ich hab selber noch nie mit voidpointern gearbeitet deswegen frag ich)

    In C wurden halt Funktionspointer als void* übergeben, was in C++ mit Funktoren oder Funktionen geht, da lediglich etwas übergeben werden muss, wo der ()-Operator angewendet werden kann. (Rückgabewert und Parameter müssen nat. auch noch stimmen..).



  • Skym0sh0 schrieb:

    beispiel:

    void ausgeben(void * stuff)    // alt...
    {
        cout << *stuff << endl;
    }
    
    template<class _T>             // neu mit template
    void ausgeben(_T stuff)
    {
        cout << stuff << endl;
    }
    

    so ist das doch oder?
    (ich hab selber noch nie mit voidpointern gearbeitet deswegen frag ich)

    Bei der C Variante müsstest du den void Zeiger stuff noch gefährlicherweise und unsichererweise und unschönerweise (lol) in einen entsprechenden explizit angegebenen Typ casten.
    Der Operator * bei einem ungecastetem void Zeiger ist übrigens so eine Sache... ich meine void kann ja nicht als Datentyp rauskommen, den gibts ja nicht.
    Könnte sogar ein Compile-fehler geben.



  • Ja Dnake für eure Antworten^^

    Ich wollte den auch nicht benutzen, nur bei paar Bibliotheken aus dem C++ wie in istream und wahrscheinlich auch in anderen streams ein

    void&
    

    definiert ist, der eine Adresse einlist und in einem Zeiger abspeichert oder bei ostream, da gibts einen void* Zeiger der eine Adresse als hexadezimal ausgibt, allerding wird dieser je nach Typ von ostream verwaltet mit dem << operator aufgerufen...

    Ich hab nur nachgefragt was es mit dem nun aufsich hat.
    Was ich nun weis und worum ich schlauer geworden bin:^^

    ➡ void* ist nur wegen Kombitaliltäs-Gründen aus C mitgenommen.

    ➡ In C++ benutzt man die Templates(Die ich sowieso schon kenne und deren Anwendung) statt des void* da der anfällig für Fehler ist.

    ➡ void und void* sind zwei verschiedene Dinge.

    ➡ Void's besitzen keinen rückgabe Typen und man sollte nicht veruschen etwas mit new und delete hinzubekommen.

    Mehr wollte ich nicht wissen^^.
    Danke Leute!

    PS: Jedoch währe bei der Funktion mit Template doch ein Fehler möglich 😉

    template<class T> 
    void function(T Parameter)
    {
        cout << Parameter << endl;
    }
    

    Damit könnte man wenn man es beabsichtigt folgendes schreiben, was zu einem Fehler führen würde^^

    int main()
    {
    void* MyVoidZeiger;
    function(MyVoidZeiger);
    }
    

    Normaler Weise gebä es hier einen Compiler fehler, oder würde da der Compiler mitmachen?^^



  • Warum sollte das ein Fehler sein? - Du gibst lediglich die Adresse eines Zeigers aus. (Halt auf ein void, aber das ist dem Zeiger egal)



  • Wikinger75 schrieb:

    ➡ void* ist nur wegen Kombitaliltäs-Gründen aus C mitgenommen.

    Falsch, void* braucht man u.a. fürs Speichermanagement. operator new gibt z.B. void* zurück.

    ➡ In C++ benutzt man die Templates(Die ich sowieso schon kenne und deren Anwendung) statt des void* da der anfällig für Fehler ist.

    Ansichtssache. Es ist eher so, dass man in C keine Templates hat und deshalb Workarounds mit void-Pointern bauen muss 😉

    ➡ Void's besitzen keinen rückgabe Typen und man sollte nicht veruschen etwas mit new und delete hinzubekommen.

    Hä?



  • Bei void-Funktionen und Rückgabe ist das so ne Sache. Man könnte sagen, sie geben void zurück, denn sowas ist ja möglich:

    void fun1() {}
    void fun2()
    {
        return fun1();
    }
    


  • Bashar schrieb:

    Falsch, void* braucht man u.a. fürs Speichermanagement. operator new gibt z.B. void* zurück.

    new gibt den einen passenden Pointer-Typen zurück, und kein void*.



  • Wirklich?
    Ich finde in 3.7.3 2 nur void* als Rückgabetyp für den globalen operator new.



  • Weil der globale _Operator_ new nur für die Speicherallozierung zuständig ist, die das _Schlüsselwort_ new (Also ein new-Ausruck) benötigt. Tachyon hat also etwas anderes gemeint als Bashar und beide haben Recht 🙂



  • Decimad schrieb:

    Weil der globale _Operator_ new nur für die Speicherallozierung zuständig ist, die das _Schlüsselwort_ new (Also ein new-Ausruck) benötigt.

    falsch.
    die operator new gibt einen void* zurück und der operator new gibt einen spezifischen zeiger zurück.

    (DIE globale funktion namens operator new gibt einen void* zurück. DER globale operator namens operator new gibt einen spezifischen zeiger zurück. der operator new benutzt die operator new, um speicher zu besorgen und ruft dann den konstruktor auf und gibt den vollkonstruierten speicher zurück.)



  • volkard schrieb:

    (DIE globale funktion namens operator new gibt einen void* zurück. DER globale operator namens operator new gibt einen spezifischen zeiger zurück. der operator new benutzt die operator new, um speicher zu besorgen und ruft dann den konstruktor auf und gibt den vollkonstruierten speicher zurück.)

    Naja, ganz klar ist das auch nicht, zumal Operatoren auch Funktionen sind. 😉

    Aber ich denke, wir wissen alle, was gemeint ist... 🙂

    T* Ptr = new T;  // new-Operator -> T*
    void* Mem  = operator new(sizeof(T)); // operator new -> void*
    


  • Nexus schrieb:

    Naja, ganz klar ist das auch nicht, zumal Operatoren auch Funktionen sind. 😉

    falsch. der operator new und der operator sizeof verarbeiten einen typen, das können funktionen nicht.

    T* Ptr = new T;  // new-Operator -> T*
    void* Mem  = operator new(sizeof(T)); // operator new -> void*
    

    aber "new-Operator" und "operator new" sind keine guten unterscheidungen, zumal der "new-Operator" den in c++ namen "operator new" trägt.



  • volkard schrieb:

    der operator new und der operator sizeof verarbeiten einen typen, das können funktionen nicht.

    Stimmt schon. Trotzdem finde ich die Unterscheidung nur auf Grund des Artikels fragwürdig. Operatoren sind schlussendlich beides...

    Am klarsten ist es wohl, wenn man einfach nur new respektive operator new sagt.



  • Nexus schrieb:

    volkard schrieb:

    der operator new und der operator sizeof verarbeiten einen typen, das können funktionen nicht.

    Stimmt schon. Trotzdem finde ich die Unterscheidung nur auf Grund des Artikels fragwürdig. Operatoren sind schlussendlich beides...

    Warum das denn?
    Ist printf() ein Operator? Wäre mir neu. Und genausowenig ist die Funktion operator new ein Operator.

    Am klarsten ist es wohl, wenn man einfach nur new respektive operator new sagt.

    aber beide heißen

    operator new
    

    das ist fakt. eure unterscheidungen sind erfunden.

    wenn du es klar machen willst, sag bei der funktion immer "Funktion" dazu und beim operator immer "Operator". ausnahmsweise ien grund für großschrift.
    also
    - die Funktion operator new
    - der Operator operator new

    aber bereits
    - die Funktion new
    - der Operator new
    wird mißverstanden.



  • volkard schrieb:

    Am klarsten ist es wohl, wenn man einfach nur new respektive operator new sagt.

    aber beide heißen

    operator new
    

    das ist fakt. eure unterscheidungen sind erfunden.

    Der Operator new heißt operator new ? In welchem Kontext?



  • volkard schrieb:

    Ist printf() ein Operator? Wäre mir neu. Und genausowenig ist die Funktion operator new ein Operator.

    Sind für dich Überladungen von operator+ und operator= auch keine Operatoren?

    volkard schrieb:

    wenn du es klar machen willst, sag bei der funktion immer "Funktion" dazu und beim operator immer "Operator". ausnahmsweise ien grund für großschrift.
    also
    - die Funktion operator new
    - der Operator operator new

    Abgesehen davon, dass das niemand tun wird (vor allem "Operator operator new" zu sagen), bezweifle ich auch, dass das wirklich Klarheit schafft.

    Und wenn man von new spricht, denkt man immer zuerst an den Operator, der Speicher anfordert und gleichzeitig Objekte konstruiert. Bei operator new denkt man schon eher an die reine Speicheranforderung. Oft erschliesst sich das sowieso aus dem Kontext. Aber mit "die operator new" und "der operator new" ist sicher niemandem geholfen...



  • Bei sauberen C++ Design, sollte sich die Anwendung von void * auf eigentlich wenige Faelle beschraenken.

    - Kompativlitaet zu C
    Klar, C arbeitet gern und oft mit untypisierten Zeigern.

    - als abstracter Datentyp, der einfach nur eindeutig sein muss ...
    Oftmals verwendet man sowas wie Handles. Die einfach ein Object beliebigen Types eindeutig referenzieren soll. meist nimmt man dann die adresse des objects und verkauft es als "handle" weiter.
    Die Dinger ueber templates zu typisieren macht eher weniger Sinn, weil der Anwender dann da typen reinbekommt, die ihm wahrscheinlich(bewusst) keinerlei Mehrwert bieten, und eine generizitaet auf seiten des anwenders einfach verhindern.
    Nen Interface zu bauen was gar nix kann, und jegliches anderes interface davon abzuleiten waer glaub ich auch ned im Sinne des OOP Gedanken ... da wuerd ich nen void * bevorzugen.

    - als Transportmedium fuer unbestimmte Daten, bzw als reine Adresse fuer die Speicherverwaltung.
    Ob man bei zeigern auf Speicherbereiche, wo man "am Anfang" gar ned weiss was drinne steht, und erst spaeter ermittelt, was genau da drinne iss und das dann castet, ob man da char * oder void * nimmt ist eher geschmackssache. Ich find void ned so verkehrt, weil es sind zwar physisch immer Bloecke von Bytes, aber logisch kann man schon weiter abstrahieren ...

    Ciao ...



  • RHBaum schrieb:

    - als abstracter Datentyp, der einfach nur eindeutig sein muss ...

    Das ist aber unsauber und in C++ nicht wirklich nötig. Meistens kennt man den Typen, und dann kann man auch typsichere Sprachmittel verwenden. Falls es doch noch Fälle gibt mit dynamischen Typen, gibt es auch entsprechende Methoden, die void* in den Schatten stellen (z.B. Boost.Any, Boost.Variant).

    RHBaum schrieb:

    Die Dinger ueber templates zu typisieren macht eher weniger Sinn, weil der Anwender dann da typen reinbekommt, die ihm wahrscheinlich(bewusst) keinerlei Mehrwert bieten, und eine generizitaet auf seiten des anwenders einfach verhindern.

    Typsicherheit ist kein Mehrwert?
    Und wie sollte durch Templates die Generizität verhindert werden?

    RHBaum schrieb:

    - als Transportmedium fuer unbestimmte Daten, bzw als reine Adresse fuer die Speicherverwaltung.
    Ob man bei zeigern auf Speicherbereiche, wo man "am Anfang" gar ned weiss was drinne steht, und erst spaeter ermittelt, was genau da drinne iss und das dann castet, ob man da char * oder void * nimmt ist eher geschmackssache. Ich find void ned so verkehrt, weil es sind zwar physisch immer Bloecke von Bytes, aber logisch kann man schon weiter abstrahieren ...

    Auch das trifft man bei hinreichender Abstraktion kaum an. Höchstens, wenn man eigene Speicherverwaltung implementiert. Es kommt aber selten vor, dass man mal x Bytes allokiert, nicht weiss, wofür die stehen, und dann willkürlich irgendeinen Typen da reinsetzt...



  • Bashar schrieb:

    Der Operator new heißt operator new ? In welchem Kontext?

    Hast recht. In keinem Kontext.
    Der Operator "+" heißt auch nicht "operator +", sondern nur "+", während die ihn implementierende Funktion "operator +" heißt. Sehr sonderbar.


Anmelden zum Antworten