Ich habe einen Code durchgesehen und festgestellt, dass die Konvention darin bestand, Zeigertypen wie zu drehen
SomeStruct*
in
typedef SomeStruct* pSomeStruct;
Gibt es einen Verdienst dafür?
Ich habe einen Code durchgesehen und festgestellt, dass die Konvention darin bestand, Zeigertypen wie zu drehen
SomeStruct*
in
typedef SomeStruct* pSomeStruct;
Gibt es einen Verdienst dafür?
Antworten:
Dies kann angemessen sein, wenn der Zeiger selbst als "Black Box" betrachtet werden kann, dh als Datenelement, dessen interne Darstellung für den Code irrelevant sein sollte.
Wenn Ihr Code den Zeiger niemals dereferenziert und Sie ihn nur an API-Funktionen weitergeben (manchmal als Referenz), reduziert das typedef nicht nur die Anzahl der *
s in Ihrem Code, sondern schlägt dem Programmierer auch vor, dass der Zeiger dies nicht tun sollte nicht wirklich eingemischt werden.
Dies macht es auch einfacher, die API in Zukunft zu ändern, wenn dies erforderlich ist. Wenn Sie beispielsweise eine ID anstelle eines Zeigers verwenden (oder umgekehrt), wird der vorhandene Code nicht beschädigt, da der Zeiger eigentlich nie dereferenziert werden sollte.
FILE*
nicht als undurchsichtiger Zeiger eingegeben wurde, besteht darin, Makros wie getc()
und putc()
die FILE-Struktur direkt zuzulassen und in den meisten Fällen den Aufwand für Funktionsaufrufe zu vermeiden.
Nach meiner Erfahrung nicht. Durch das Ausblenden des ' *
' ist der Code schwer zu lesen.
Device
und Sie typedef
ihn auf Device_Ptr
(anstatt ihn verwenden zu müssen Device *
) oder ähnliches, ist er ziemlich lesbar. Ich sehe viele gut ausgereifte und ziemlich große Bibliotheken, die das tun. Insbesondere in C ++, wenn Sie Vorlagen hinzufügen, kann dies Ihren Fingern die Eingabe ersparen.
myTypePtr
oder myTypeRef
übernehmen. : P
Das einzige Mal, dass ich einen Zeiger innerhalb des typedef verwende, ist der Umgang mit Zeigern auf Funktionen:
typedef void (*SigCatcher(int, void (*)(int)))(int);
typedef void (*SigCatcher)(int);
SigCatcher old = signal(SIGINT, SIG_IGN);
Ansonsten finde ich sie eher verwirrend als hilfreich.
signal()
Funktion, nicht auf den Signalfänger. Es könnte klarer gemacht werden (unter Verwendung des oben korrigierten SigCatcher
Typs), indem geschrieben wird:
typedef SigCatcher (*SignalFunction)(int, SigCatcher);
Oder um die signal()
Funktion zu deklarieren :
extern SigCatcher signal(int, SigCatcher);
Das heißt, a SignalFunction
ist ein Zeiger auf eine Funktion, die zwei Argumente (an int
und a SigCatcher
) akzeptiert und a zurückgibt SigCatcher
. Und signal()
selbst ist eine Funktion, die zwei Argumente (an int
und a SigCatcher
) akzeptiert und a zurückgibt SigCatcher
.
sig_t
passt zu meinem SigCatcher
, und ja, es vereinfacht die Erklärung von signal()
enorm.
Dies kann Ihnen helfen, einige Fehler zu vermeiden. Zum Beispiel im folgenden Code:
int* pointer1, pointer2;
pointer2 ist kein int * , es ist einfach int . Aber mit typedefs wird das nicht passieren:
typedef int* pInt;
pInt pointer1, pointer2;
Sie sind beide jetzt int * .
int *p1 = NULL, *p2 = NULL;
*
keine Rolle.
Meine Antwort ist ein klares "Nein".
Warum?
Zunächst einmal tauschen Sie einfach ein einzelnes Zeichen *
gegen ein anderes einzelnes Zeichen aus p
. Das ist null Verstärkung. Dies allein sollte Sie davon abhalten, dies zu tun, da es immer schlecht ist, zusätzliche Dinge zu tun, die sinnlos sind.
Zweitens, und das ist der wichtige Grund, der *
trägt was bedeutet , dass zu verstecken ist nicht gut . Wenn ich etwas an eine solche Funktion übergebe
void foo(SomeType bar);
void baz() {
SomeType myBar = getSomeType();
foo(myBar);
}
Ich erwarte nicht, dass die Bedeutung von myBar
geändert wird, indem man sie weitergibt foo()
. Immerhin gehe ich an Wert vorbei, foo()
sehe also immer nur eine Kopie von myBar
richtig? Nicht, wenn SomeType
Alias eine Art Zeiger bedeutet!
Dies gilt sowohl für C-Zeiger als auch für intelligente C ++ - Zeiger: Wenn Sie die Tatsache verbergen, dass es sich um Zeiger auf Ihre Benutzer handelt, entsteht Verwirrung, die völlig unnötig ist. Also bitte nicht alias deine Zeiger.
(Ich glaube, die Gewohnheit, Zeigertypen zu tippen, ist nur ein fehlgeleiteter Versuch, zu verbergen, wie viele Sterne man als Programmierer hat http://wiki.c2.com/?ThreeStarProgrammer .)
void *vp=/*...*/; foo(vp);
oder gibt foo(0);
. (Typedefs für Aufzählungen und numerische Typedefs für Dinge, die niemals als Zahlen angezeigt werden sollten, haben ähnliche Probleme).
Das ist eine Frage des Stils. Sie sehen diese Art von Code sehr häufig in den Windows-Header-Dateien. Sie bevorzugen jedoch eher die Version in Großbuchstaben als das Präfix in Kleinbuchstaben p.
Persönlich vermeide ich diese Verwendung von typedef. Es ist viel klarer, wenn der Benutzer ausdrücklich sagt, dass er ein Foo * als PFoo möchte. Typedefs sind heutzutage am besten geeignet, um STL lesbar zu machen :)
typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;
Es kommt (wie so viele Antworten) darauf an.
In C ist dies sehr häufig, da Sie versuchen zu verschleiern, dass ein Objekt ein Zeiger ist. Sie versuchen zu implizieren, dass dies das Objekt ist, das alle Ihre Funktionen manipulieren (wir wissen, dass es ein Zeiger 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 befindet sich wahrscheinlich ein Zeiger auf ein Objekt.
In C ++ ist dies nicht mehr erforderlich.
Hauptsächlich, weil wir Dinge als Referenz weitergeben können und die Methoden in die Klassendeklaration aufgenommen werden.
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.
Nein.
Es wird dein Leben unglücklich machen, sobald du es mischst const
typedef foo *fooptr;
const fooptr bar1;
const foo *bar2
Sind bar1
und bar2
der gleiche Typ?
Und ja, ich zitiere nur Herb Sutters Guru. Viel Wahrheit sprach sie. ;)
- Bearbeiten -
Link zum zitierten Artikel hinzufügen.
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835
Die Diskussion wurde unter der Annahme geführt, dass die Sprache von Interesse C ist. Auswirkungen für C ++ wurden nicht berücksichtigt.
Die Frage Größe einer Struktur, die als Zeiger definiert ist, wirft ein interessantes Seitenlicht auf die Verwendungtypedef
von (Struktur-) Zeigern.
Betrachten Sie die Definition des taglosen konkreten (nicht undurchsichtigen) Strukturtyps:
typedef struct { int field1; double field2; } *Information;
Die Details der Mitglieder sind für diese Diskussion völlig tangential. Alles, was zählt, ist, dass dies kein undurchsichtiger Typ ist typedef struct tag *tag;
(und Sie können solche undurchsichtigen Typen nicht über ein typedef
ohne Tag definieren).
Die Frage lautet: Wie können Sie die Größe dieser Struktur ermitteln?
Die kurze Antwort lautet "nur über eine Variable des Typs". Es gibt kein Tag, mit dem verwendet werden kann sizeof(struct tag)
. Sie können zum Beispiel nicht sinnvoll schreiben undsizeof(*Information)
sizeof(Information *)
die Größe eines Zeigers auf den Zeigertyp, nicht die Größe des Strukturtyps.
Wenn Sie eine solche Struktur zuweisen möchten, können Sie nur eine dynamische Zuordnung erstellen (oder Ersatztechniken, die die dynamische Zuordnung nachahmen). Es gibt keine Möglichkeit, eine lokale Variable des Strukturtyps zu erstellen, deren Zeiger aufgerufen werden Information
, und es gibt auch keine Möglichkeit, einen Dateibereich (global oder) zu erstellenstatic
) des Strukturtyps , noch eine Möglichkeit, eine solche Struktur einzubetten (as im Gegensatz zu einem Zeiger auf eine solche Struktur) in eine andere Struktur oder einen anderen Vereinigungstyp.
Sie können - müssen - schreiben:
Information info = malloc(sizeof(*info));
Abgesehen von der Tatsache, dass der Zeiger in der versteckt ist typedef
, ist dies eine gute Praxis - wenn die Art voninfo
Änderungen ändert, bleibt die Größenzuweisung korrekt. In diesem Fall ist dies jedoch auch die einzige Möglichkeit, die Größe der Struktur zu ermitteln und die Struktur zuzuweisen. Und es gibt keine andere Möglichkeit, eine Instanz der Struktur zu erstellen.
Es hängt von Ihren Zielen ab.
Dies ist kein undurchsichtiger Typ - die Details der Struktur müssen definiert werden, wenn der Zeigertyp typedef
'd' ist.
Dieser Typ kann nur mit dynamischer Speicherzuordnung verwendet werden.
Es ist ein Typ, der namenlos ist. Der Zeiger auf den Strukturtyp hat einen Namen, der Strukturtyp selbst jedoch nicht.
Wenn Sie die dynamische Zuweisung erzwingen möchten, scheint dies eine Möglichkeit zu sein.
Im Großen und Ganzen ist es jedoch wahrscheinlicher, Verwirrung und Angst zu verursachen als Erleuchtung.
Es ist im Allgemeinen eine schlechte Idee typedef
, einen Zeiger auf einen taglosen Strukturtyp zu definieren.
In diesem Fall können Sie keine STL-Container von const pSomeStruct erstellen, da der Compiler Folgendes liest:
list<const pSomeStruct> structs;
wie
list<SomeStruct * const> structs;
Dies ist kein legaler STL-Container, da die Elemente nicht zuweisbar sind.
Siehe diese Frage .
list<const ordinary_type>
, da es sowieso nicht funktionieren würde, also gibt es keine Verwirrung.
ordinary_type
. list<const SomeStruct*>
ist ebenso gültig wie const SomeStruct*
CopyAssignable.
Die Win32-API erledigt dies mit nahezu jeder Struktur (wenn nicht allen).
POINT => *LPPOINT
WNDCLASSEX => *LPWNDCLASSEX
RECT => *LPRECT
PRINT_INFO_2 => *LPPRINT_INFO_2
Es ist schön, wie konsistent es ist, aber meiner Meinung nach fügt es keine Eleganz hinzu.
LP
steht für langen Zeiger und die Kurzschreibweise LPPOINT ptr;
war nichts weiter als das, eine handliche Abkürzung für POINT FAR *ptr;
, FAR
Erweiterung zu far
und später zu __far
. Das alles ist ziemlich unelegant, aber früher war es noch schlimmer.
Der Zweck von typedef besteht darin, die Implementierungsdetails auszublenden, aber das Eingeben der Zeiger-Eigenschaft verbirgt zu viel und erschwert das Lesen / Verstehen des Codes. Also bitte tu das nicht.
Wenn Sie Implementierungsdetails ausblenden möchten (was häufig sinnvoll ist), verbergen Sie den Zeigerteil nicht. Nehmen Sie 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 Zeiger auf eine Struktur zurück FILE
(für die Sie die Implementierungsdetails nicht kennen). Vielleicht FILE
ist es kein so gutes Beispiel, weil es in diesem Fall mit einem pFILE-Typ hätte funktionieren können, der die Tatsache verbarg, dass es sich um einen Zeiger handelt.
pFILE fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, pFILE stream);
Dies würde jedoch nur funktionieren, weil Sie nie mit dem Inhalt herumspielen, auf den direkt verwiesen wird. In dem Moment, in dem Sie einen Zeiger eingeben, den Sie an einigen Stellen ändern, wird der Code meiner Erfahrung nach sehr schwer zu lesen.
Vor einiger Zeit hätte ich diese Frage mit "Nein" beantwortet. Mit dem Aufkommen intelligenter Zeiger werden Zeiger nicht mehr immer mit einem Stern '*' definiert. Es ist also nichts Offensichtliches daran, dass ein Typ ein Zeiger ist oder nicht.
Jetzt würde ich also sagen: Es ist in Ordnung, Zeiger zu tippen, solange klargestellt wird, dass es sich um einen "Zeigertyp" handelt. Das bedeutet, dass Sie ein Präfix / Suffix speziell dafür verwenden müssen. Nein, "p" ist beispielsweise kein ausreichendes Präfix. Ich würde wahrscheinlich mit "ptr" gehen.