Ist es eine gute Idee, pointers zu schreiben?

Ich habe einen Code durchgesehen und festgestellt, dass die Konvention Pointer-Typen wie z

SomeStruct* 

in

 typedef SomeStruct* pSomeStruct; 

Gibt es dafür einen Verdienst?

Dies kann angebracht sein, wenn der pointers selbst als eine “Black Box” betrachtet werden kann, dh als eine Dateneinheit, deren interne Darstellung für den Code irrelevant sein sollte.

Wenn Ihr Code den pointers niemals dereferenziert und Sie ihn nur um API-functionen (manchmal durch Verweis) weitergeben, verringert der typedef nicht nur die Anzahl von * s in Ihrem Code, sondern schlägt dem Programmierer auch vor, den pointers zu verwenden sollte nicht wirklich mit eingemischt werden.

Dies macht es auch leichter, die API in der Zukunft zu ändern, wenn es notwendig ist, zum Beispiel eine ID anstelle eines pointerss zu verwenden (oder umgekehrt). Da der pointers niemals von vornherein dereferenziert werden sollte, wird bestehender Code nicht unterbrochen.

Nicht in meiner Erfahrung. Das Ausblenden des ‘ * ‘ erschwert das Lesen des Codes.

Die einzige Zeit, die ich einen pointers innerhalb des typedef verwende, ist, wenn ich mit pointersn auf functionen handle:

 typedef void (*SigCatcher(int, void (*)(int)))(int); 

 typedef void (*SigCatcher)(int); SigCatcher old = signal(SIGINT, SIG_IGN); 

Ansonsten finde ich sie verwirrender als hilfreich.


Die stroke-out-Deklaration ist der richtige Typ für einen pointers auf die signal() function, nicht auf den Signal-Catcher. Es könnte klarer gemacht werden (mit dem korrigierten SigCatcher Typ oben), indem man schreibt:

  typedef SigCatcher (*SignalFunction)(int, SigCatcher); 

Oder um die signal() function zu deklarieren:

  extern SigCatcher signal(int, SigCatcher); 

Das heißt, eine SignalFunction ist ein pointers auf eine function, die zwei Argumente (einen int und einen SigCatcher ) und einen SigCatcher . Und signal() selbst ist eine function, die zwei Argumente (ein int und ein SigCatcher ) SigCatcher und einen SigCatcher .

Dies kann Ihnen helfen, einige Fehler zu vermeiden. Zum Beispiel im folgenden Code:

 int* pointer1, pointer2; 

pointers2 ist kein int * , es ist einfach int . Aber mit typedefs wird das nicht passieren:

 typedef int* pInt; pInt pointer1, pointer2; 

Sie sind beide jetzt * .

Es hängt (wie so viele Antworten) davon ab.

In C ist dies sehr üblich, da Sie versuchen zu verschleiern, dass ein Objekt ein pointers ist. Sie versuchen zu implizieren, dass dies das Objekt ist, das alle Ihre functionen manipulieren (wir wissen, dass es ein pointers darunter ist, aber es repräsentiert das Objekt, das Sie manipulieren).

 MYDB db = MYDBcreateDB("Plop://djdjdjjdjd"); MYDBDoSomthingWithDB(db,5,6,7); CallLocalFuc(db); // if db is not a pointer things could be complicated. MYDBdestroyDB(db); 

Unter MYDB ist wahrscheinlich ein pointers auf irgendein Objekt.

In C ++ ist dies nicht mehr erforderlich.
Vor allem, weil wir die Dinge durch Verweis weitergeben können und die Methoden in die classndeklaration integriert sind.

 MyDB db("Plop://djdjdjjdjd"); db.DoSomthingWithDB(5,6,7); CallLocalFuc(db); // This time we can call be reference. db.destroyDB(); // Or let the destructor handle it. 

Das ist eine Frage des Stils. Sie sehen diese Art von Code sehr häufig in den Windows-Header-Dateien. Sie neigen jedoch dazu, die Großbuchstabenversion zu bevorzugen, anstatt sie mit einem Kleinbuchstaben p zu versehen.

Persönlich vermeide ich diese Verwendung von typedef. Es ist viel klarer, wenn der Benutzer explizit sagt, dass er ein Foo * als PFoo möchte. Typedefs sind heutzutage am besten geeignet, um STL lesbar zu machen 🙂

 typedef stl::map> NameToFooMap; 

Typedef wird verwendet, um Code lesbarer zu machen, aber das Erstellen von pointers als Typedef wird die Verwirrung erhöhen. Bessere Tipperrors vermeiden.

Wenn Sie dies tun, können Sie keine STL-Container mit const pSomeStruct erstellen, da der Compiler liest:

 list structs; 

wie

 list structs; 

Dies ist kein zulässiger STL-Container, da die Elemente nicht zuweisbar sind.

Siehe diese Frage .

Meine Antwort ist ein klares “Nein”.

Warum?

Zuerst tauscht man einfach ein einzelnes Zeichen * gegen ein anderes einzelnes Zeichen aus. Das ist keine Verstärkung. Dies allein sollte dich davon abhalten, dies zu tun, da es immer schlecht ist, zusätzliche Sachen zu machen, die sinnlos sind.

Zweitens, und das ist der wichtige Grund, trägt das * Bedeutung, die nicht gut zu verbergen ist . Wenn ich etwas an eine function wie diese weitergebe

 void foo(SomeType bar); void baz() { SomeType myBar = getSomeType(); foo(myBar); } 

Ich erwarte nicht, dass die Bedeutung von myBar geändert wird, indem ich es an foo() . Immerhin passiere ich den Wert, also sieht foo() immer nur eine Kopie von myBar oder? Nicht wenn SomeType ist, um irgendeine Art von pointers zu bedeuten!

Dies gilt sowohl für C-pointers als auch für C ++ – Smartpointer: Wenn Sie die Tatsache, dass es sich um pointers auf Ihre Benutzer handelt, verbergen, erzeugen Sie Verwirrung, die völlig unnötig ist. Also, bitte, alias deine pointers nicht.

(Ich glaube, die Angewohnheit, pointerstypen zu schreiben, ist nur ein fehlgeleiteter Versuch, zu verbergen, wie viele Sterne man als Programmierer hat. http://wiki.c2.com/?ThreeStarProgrammer .)

Win32 API tut dies mit fast jeder Struktur (wenn nicht alle)

 POINT => *LPPOINT WNDCLASSEX => *LPWNDCLASSEX RECT => *LPRECT PRINT_INFO_2 => *LPPRINT_INFO_2 

Es ist schön, wie es konsistent ist, aber meiner Meinung nach bringt es keine Eleganz.

Vor einiger Zeit hätte ich diese Frage mit “Nein” beantwortet. Mit dem Aufkommen von Smartpointern werden pointers nicht immer mit einem Stern ‘*’ definiert. Es ist also nichts offensichtlich, dass ein Typ ein pointers ist oder nicht.

So, jetzt würde ich sagen: es ist in Ordnung, def pointers zu schreiben, solange es sehr klar ist, dass es ein “pointerstyp” ist. Das bedeutet, dass Sie ein Präfix / Suffix speziell dafür verwenden müssen. Nein, “p” ist zum Beispiel kein ausreichendes Präfix. Ich würde wahrscheinlich mit “ptr” gehen.

Die Diskussion wird unter der Annahme fortgesetzt, dass die Sprache von Interesse C ist. Verzweigungen für C ++ wurden nicht berücksichtigt.

Verwendung eines pointerss typedef für eine nicht markierte Struktur

Die Frage Größe einer Struktur, die als pointers definiert ist, wirft ein interessantes Seitenlicht bei der Verwendung von typedef für (Struktur-) pointers auf.

Berücksichtigen Sie die Strukturtypdefinition tagless beton (nicht opak):

 typedef struct { int field1; double field2; } *Information; 

Die Details der Mitglieder sind völlig tangential zu dieser Diskussion; Alles was zählt ist, dass dies kein undurchsichtiger Typ wie typedef struct tag *tag; (Und Sie können solche undurchsichtigen Typen nicht über ein typedef ohne ein Tag definieren).

Die Frage ist, wie kann man die Größe dieser Struktur finden?

Die kurze Antwort lautet “nur über eine Variable des Typs”. Es gibt kein Tag, das mit sizeof(struct tag) . Sie können beispielsweise nicht sinnvoll sizeof(*Information) schreiben, und sizeof(Information *) ist die Größe eines pointerss auf den pointerstyp, nicht die Größe des Strukturtyps.

Wenn Sie eine solche Struktur zuweisen möchten, können Sie nur eine dynamische Struktur (oder Ersatztechniken, die die dynamische Zuordnung nachahmen) erstellen. Es gibt keine Möglichkeit, eine lokale Variable des Strukturtyps zu erstellen, deren pointers Information sind. Es gibt auch keine Möglichkeit, eine Dateibereichs (globale oder static ) Variable des Strukturtyps zu erstellen, und es gibt auch keine Möglichkeit, eine solche Struktur einzubetten (im Gegensatz zu einem pointers auf eine solche Struktur) in eine andere Struktur oder einen Union-Typ.

Sie können – müssen – schreiben:

 Information info = malloc(sizeof(*info)); 

Abgesehen davon, dass der pointers in typedef versteckt ist, ist dies eine gute Übung – wenn sich der Typ der info ändert, bleibt die typedef genau. Aber in diesem Fall ist es auch die einzige Möglichkeit, die Größe der Struktur zu erhalten und die Struktur zuzuordnen. Und es gibt keine andere Möglichkeit, eine Instanz der Struktur zu erstellen.

Ist das schädlich?

Es hängt von deinen Zielen ab.

Dies ist kein undurchsichtiger Typ – die Details der Struktur müssen definiert werden, wenn der typedef ‘d ist.

Es ist ein Typ, der nur mit dynamischer Speicherzuweisung verwendet werden kann.

Es ist ein Typ, der namenlos ist. Der pointers auf den Strukturtyp hat einen Namen, der Strukturtyp selbst jedoch nicht.

Wenn Sie die dynamische Zuordnung erzwingen möchten, scheint dies eine Möglichkeit zu sein.

Im Großen und Ganzen ist es jedoch eher möglich, Verwirrung und Angst zu verursachen als Erleuchtung.

Zusammenfassung

Es ist im Allgemeinen eine schlechte Idee, typedef zu verwenden, um einen pointers auf einen Tagless- typedef zu definieren.

Der Zweck von typedef besteht darin, die Implementierungsdetails auszublenden, aber typedef die pointerseigenschaft verbirgt zu viel und macht den Code schwerer lesbar. Also bitte tu das nicht.


Wenn Sie Implementierungsdetails ausblenden möchten (was oft eine gute Sache ist), sollten Sie den pointersteil nicht ausblenden. Nehmen wir zum Beispiel den Prototyp für die Standardschnittstelle FILE :

 FILE *fopen(const char *filename, const char *mode); char *fgets(char *s, int size, FILE *stream); 

Hier gibt fopen einen pointers auf eine Struktur FILE (für die Sie die Implementierungsdetails nicht kennen). Vielleicht ist FILE nicht so ein gutes Beispiel, denn in diesem Fall hätte es mit einem pFILE-Typ funktionieren können, der die Tatsache verbarg, dass es ein pointers ist.

 pFILE fopen(const char *filename, const char *mode); char *fgets(char *s, int size, pFILE stream); 

Das würde jedoch nur funktionieren, weil Sie sich nie mit dem Inhalt herumschlagen, auf den direkt verwiesen wird. In dem Moment, in dem Sie einen pointers eingeben, den Sie an einigen Stellen ändern, wird der Code in meiner Erfahrung sehr schwer zu lesen.