Ich habe etwas Code durchgesehen und festgestellt, dass die Konvention darin bestand, Zeigertypen zu drehen
SomeStruct*
in
typedef SomeStruct* pSomeStruct;
Gibt es einen Verdienst dafür?
Dies kann sinnvoll 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 der Code den Zeiger mit never dereferenziert und Sie ihn einfach um API-Funktionen übergeben (manchmal durch Referenz), dann reduziert der Typedef nicht nur die Anzahl der *
s in Ihrem Code, sondern schlägt dem Programmierer auch vor dass der Zeiger nicht wirklich mit eingemischt werden sollte.
Dies macht es auch einfacher, die API in der Zukunft zu ändern, wenn dies erforderlich ist. Wenn Sie beispielsweise zur Verwendung einer ID anstelle eines Zeigers wechseln (oder umgekehrt), wird vorhandener Code nicht beschädigt, da der Zeiger nie zuvor dereferenziert werden sollte.
Meiner Erfahrung nach nicht. Das Ausblenden des '*
' erschwert das Lesen des Codes.
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()
, nicht auf den Signalfänger. Es könnte klarer gemacht werden (unter Verwendung des oben korrigierten Typs SigCatcher
), indem Folgendes geschrieben wird: typedef SigCatcher (*SignalFunction)(int, SigCatcher);
Oder deklarieren Sie die Funktion signal()
:
extern SigCatcher signal(int, SigCatcher);
Das heißt, ein SignalFunction
ist ein Zeiger auf eine Funktion, die zwei Argumente akzeptiert (ein int
und ein SigCatcher
) und ein SigCatcher
zurückgibt. Und signal()
selbst ist eine Funktion, die zwei Argumente akzeptiert (ein int
und ein SigCatcher
) und ein SigCatcher
zurückgibt.
Dies kann Ihnen helfen, einige Fehler zu vermeiden. Zum Beispiel im folgenden Code:
int* pointer1, pointer2;
pointer2 ist kein int * , es ist einfach int . Bei Typedefs wird dies jedoch nicht passieren:
typedef int* pInt;
pInt pointer1, pointer2;
Sie sind beide int * now.
Meine Antwort ist ein klares "Nein".
Warum?
Zunächst tauschen Sie einfach ein einzelnes Zeichen *
Gegen ein anderes einzelnes Zeichen p
aus. Das ist Null Gewinn. Dies allein sollte Sie davon abhalten, da es immer schlecht ist, zusätzliche Dinge zu tun, die sinnlos sind.
Zweitens, und das ist der wichtige Grund, der *
Hat eine Bedeutung, die nicht gut zu verbergen ist . Wenn ich so etwas an eine Funktion übergebe
void foo(SomeType bar);
void baz() {
SomeType myBar = getSomeType();
foo(myBar);
}
Ich erwarte nicht, dass die Bedeutung von myBar
durch Übergabe an foo()
geändert wird. Schließlich übergebe ich einen Wert, sodass foo()
immer nur eine Kopie von myBar
sieht, oder? Nicht, wenn SomeType
eine Art Zeiger bedeutet!
Dies gilt sowohl für C-Zeiger als auch für C++ - Smart-Zeiger: Wenn Sie die Tatsache verbergen, dass es sich um Zeiger auf Ihre Benutzer handelt, entsteht eine völlig unnötige Verwirrung. Also, bitte, verfälschen Sie nicht Ihre Zeiger.
(Ich glaube, die Gewohnheit, Zeigertypen zu tippen, ist nur ein irrtümlicher Versuch, zu verbergen, wie viele Sterne man als Programmierer hat http://wiki.c2.com/?ThreeStarProgrammer .)
Es hängt (wie so viele Antworten) davon ab.
In C ist dies sehr üblich, wenn 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 sich darunter ein Zeiger befindet, der jedoch das Objekt darstellt, das Sie bearbeiten).
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 deshalb, weil wir Dinge als Referenz übergeben können und die Methoden in die Klassendeklaration integriert 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.
Das ist eine Frage des Stils. Sie sehen diese Art von Code sehr häufig in den Windows-Headerdateien. Sie neigen jedoch dazu, die gesamte Großbuchstaben-Version zu bevorzugen, anstatt mit einem Kleinbuchstaben p vorzugehen.
Ich persönlich vermeide diese Verwendung von Typedef. Es ist viel klarer, wenn der Benutzer explizit sagt, dass er ein Foo * als PFoo will. Typedefs sind heutzutage am besten geeignet, um STL lesbar zu machen :)
typedef stl::map<stl::wstring,CAdapt<CComPtr<IFoo>> NameToFooMap;
Die Diskussion wurde mit der Annahme geführt, dass die Sprache von Interesse C ist. Die Auswirkungen auf C++ wurden nicht berücksichtigt.
Die Frage Größe einer als Zeiger definierten Struktur wirft ein interessantes Licht auf die Verwendung von typedef
für (Struktur-) Zeiger.
Betrachten Sie die Definition des Strukturtyps tagless concrete (nicht opak):
typedef struct { int field1; double field2; } *Information;
Die Einzelheiten der Mitglieder sind für diese Diskussion völlig tangential. Alles, was zählt, ist, dass dies kein undurchsichtiger Typ wie typedef struct tag *tag;
ist (und Sie können solche undurchsichtigen Typen nicht über ein typedef
ohne ein Tag definieren).
Die Frage ist, wie man die Größe dieser Struktur findet.
Die kurze Antwort lautet "nur über eine Variable des Typs". Es gibt kein Tag, das mit sizeof(struct tag)
verwendet werden kann. Sie können nicht sinnvoll schreiben Beispiel: und sizeof(*Information)
sizeof(Information *)
ist die Größe eines Zeigers auf den Zeigertyp und nicht die Größe des Strukturtyps.
Wenn Sie eine solche Struktur zuweisen möchten, können Sie sie nur über die dynamische Zuweisung (oder Ersatztechniken, die die dynamische Zuweisung imitieren) erstellen. Es gibt keine Möglichkeit, eine lokale Variable des Strukturtyps zu erstellen, deren Zeiger Information
heißen, und es gibt keine Möglichkeit, eine Dateibereichsvariable (global oder static
) des Strukturtyps zu erstellen. Es gibt auch keine Möglichkeit, eine solche Struktur (im Gegensatz zu einem Zeiger auf eine solche Struktur) in eine andere Struktur oder einen anderen Vereinigungstyp einzubetten.
Sie können - müssen - schreiben:
Information info = malloc(sizeof(*info));
Abgesehen von der Tatsache, dass der Zeiger in typedef
verborgen ist, ist dies eine gute Praxis - wenn sich der Typ von info
ändert, bleibt die Größenzuweisung korrekt. In diesem Fall ist es 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 Speicherzuweisung 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 Zuordnung erzwingen möchten, ist dies anscheinend eine Möglichkeit, dies zu tun.
Insgesamt ist es jedoch wahrscheinlicher, dass es Verwirrung und Angst hervorruft als Erleuchtung.
Es ist im Allgemeinen eine schlechte Idee, typedef
zu verwenden, um einen Zeiger auf einen taglosen Strukturtyp zu definieren.
Typedef wird verwendet, um Code lesbarer zu machen. Wenn Sie jedoch den Zeiger als Typedef verwenden, erhöht sich die Verwirrung. Vermeiden Sie besser Typedef-Zeiger.
Wenn Sie dies tun, 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 zulässiger STL-Container, da die Elemente nicht zuweisbar sind.
Siehe diese Frage .
Win32-API macht dies mit fast jeder Struktur (wenn nicht allen)
POINT => *LPPOINT
WNDCLASSEX => *LPWNDCLASSEX
RECT => *LPRECT
PRINT_INFO_2 => *LPPRINT_INFO_2
Es ist schön, wie es konsistent ist, aber meiner Meinung nach fügt es keine Eleganz hinzu.
Vor einiger Zeit hätte ich diese Frage mit "Nein" beantwortet. Mit dem Aufkommen von intelligenten Zeigern werden Zeiger nicht mehr immer mit einem Stern '*' definiert. Es ist also nichts offensichtlich, dass ein Typ ein Zeiger ist oder nicht.
Nun würde ich sagen: Es ist gut für Zeiger mit Typedef, solange deutlich gemacht wird, dass es sich um einen Zeigertyp handelt. Das heißt, Sie müssen dafür ein Präfix/Suffix verwenden. Nein, "p" ist zum Beispiel kein ausreichendes Präfix. Ich würde wahrscheinlich mit "ptr" gehen.
Der Zweck von typedef besteht darin, die Implementierungsdetails auszublenden, aber die Zeiger-Eigenschaft wird durch das Eingeben der Zeiger-Eigenschaft zu sehr verborgen und macht es schwieriger, den Code zu lesen/zu verstehen.
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 ein pointer an eine Struktur FILE
zurück (für die Sie die Implementierungsdetails nicht kennen). Vielleicht ist FILE
kein so gutes Beispiel, denn in diesem Fall hätte es mit einem pFILE-Typ funktionieren können, der die Tatsache verdeckte, dass es sich um einen Zeiger handelt.
pFILE fopen(const char *filename, const char *mode);
char *fgets(char *s, int size, pFILE stream);
Das funktioniert jedoch nur, weil Sie sich nie mit dem Inhalt herumschlagen, auf den direkt verwiesen wird. In dem Moment, in dem Sie einen Zeiger eingeben, an dem Sie den Code an einigen Stellen ändern, wird es nach meiner Erfahrung sehr schwer zu lesen.
Nein.
In dem Moment, in dem Sie es mit const
mischen, wird Ihr Leben unglücklich.
typedef foo *fooptr;
const fooptr bar1;
const foo *bar2
Sind bar1
und bar2
vom gleichen Typ?
Und ja, ich zitiere nur Herb Sutters Guru. Viel Wahrheit hat sie gesagt. ;)
- Bearbeiten -
Link zu zitiertem Artikel hinzufügen.
http://www.drdobbs.com/conversationsa-midsummernights-madness/184403835