Aliase und Typedefs (C++)

Sie können eine Aliasdeklaration verwenden, um einen Namen zu deklarieren, der als Synonym für einen zuvor deklarierten Typ verwendet werden soll. (Dieser Mechanismus wird auch informell als Typalias bezeichnet). Sie können diesen Mechanismus auch verwenden, um eine Aliasvorlage zu erstellen, die für benutzerdefinierte Zuweisungen nützlich sein kann.

Syntax

using identifier = type;

Hinweise

identifier
Der Name des Alias.

type
Der Typbezeichner, für den Sie einen Alias erstellen.

Ein Alias führt keinen neuen Typ ein und kann die Bedeutung eines vorhandenen Typnamens nicht ändern.

Die einfachste Form eines Alias entspricht dem typedef Mechanismus von C++03:

// C++11
using counter = long;

// C++03 equivalent:
// typedef long counter;

Beide Formulare ermöglichen die Erstellung von Variablen vom Typ counter. Nützlicherer wäre ein Typalias wie dieser für std::ios_base::fmtflags:

// C++11
using fmtfl = std::ios_base::fmtflags;

// C++03 equivalent:
// typedef std::ios_base::fmtflags fmtfl;

fmtfl fl_orig = std::cout.flags();
fmtfl fl_hex = (fl_orig & ~std::cout.basefield) | std::cout.showbase | std::cout.hex;
// ...
std::cout.flags(fl_hex);

Aliase funktionieren auch mit Funktionszeigern, sind aber wesentlich besser lesbar als die entsprechende Typedef:

// C++11
using func = void(*)(int);

// C++03 equivalent:
// typedef void (*func)(int);

// func can be assigned to a function pointer value
void actual_function(int arg) { /* some code */ }
func fptr = &actual_function;

Eine Einschränkung des typedef Mechanismus besteht darin, dass er nicht mit Vorlagen funktioniert. Die Typaliassyntax in C++11 ermöglicht jedoch die Erstellung von Aliasvorlagen:

template<typename T> using ptr = T*;

// the name 'ptr<T>' is now an alias for pointer to T
ptr<int> ptr_int;

Beispiel

Das folgende Beispiel zeigt die Verwendung einer Aliasvorlage mit einer benutzerdefinierten Zuweisung, in diesem Fall einem ganzzahligen Vektortyp. Sie können jeden beliebigen Typ int ersetzen, um einen geeigneten Alias zu erstellen, um die komplexen Parameterlisten in Ihrem Standard Funktionalen Code auszublenden. Wenn Sie den benutzerdefinierten Zuweisungscode im gesamten Code verwenden, können Sie die Lesbarkeit verbessern und das Risiko verringern, dass Fehler auftreten, die durch Tippfehler verursacht werden.

#include <stdlib.h>
#include <new>

template <typename T> struct MyAlloc {
    typedef T value_type;

    MyAlloc() { }
    template <typename U> MyAlloc(const MyAlloc<U>&) { }

    bool operator==(const MyAlloc&) const { return true; }
    bool operator!=(const MyAlloc&) const { return false; }

    T * allocate(const size_t n) const {
        if (n == 0) {
            return nullptr;
        }

        if (n > static_cast<size_t>(-1) / sizeof(T)) {
            throw std::bad_array_new_length();
        }

        void * const pv = malloc(n * sizeof(T));

        if (!pv) {
            throw std::bad_alloc();
        }

        return static_cast<T *>(pv);
    }

    void deallocate(T * const p, size_t) const {
        free(p);
    }
};

#include <vector>
using MyIntVector = std::vector<int, MyAlloc<int>>;

#include <iostream>

int main ()
{
    MyIntVector foov = { 1701, 1764, 1664 };

    for (auto a: foov) std::cout << a << " ";
    std::cout << "\n";

    return 0;
}
1701 1764 1664

TypeDefs

Eine typedef Deklaration führt einen Namen ein, der innerhalb seines Gültigkeitsbereichs zu einem Synonym für den Typ wird, der vom Typdeklarationsteil der Deklaration angegeben wird.

Sie können typedef-Deklarationen verwenden, um kürzere oder aussagekräftigere Namen für Typen zu erstellen, die bereits von der Sprache definiert wurden, oder für Typen, die Sie deklariert haben. Mithilfe von typedef-Namen können Sie die Implementierungsdetails kapseln, die sich möglicherweise ändern.

Im Gegensatz zu den classenumstructunionDeklarationen führen Deklarationen typedef keine neuen Typen ein; sie führen neue Namen für vorhandene Typen ein.

Namen, die mit typedef demselben Namespace deklariert wurden wie andere Bezeichner (mit Ausnahme von Anweisungsbezeichnungen). Daher können sie nicht denselben Bezeichner wie ein zuvor deklarierter Name verwenden, außer in einer Klassentypdeklaration. Betrachten Sie das folgende Beispiel:

// typedef_names1.cpp
// C2377 expected
typedef unsigned long UL;   // Declare a typedef name, UL.
int UL;                     // C2377: redefined.

Die Regeln zum Ausblenden von Namen, die sich auf andere Bezeichner beziehen, regeln auch die Sichtbarkeit von Namen, die mithilfe typedefdeklariert werden. Deshalb ist das folgenden Beispiel in C++ zulässig:

// typedef_names2.cpp
typedef unsigned long UL;   // Declare a typedef name, UL
int main()
{
   unsigned int UL;   // Redeclaration hides typedef name
}

// typedef UL back in scope

Eine andere Instanz des Ausblendens von Namen:

// typedef_specifier1.cpp
typedef char FlagType;

int main()
{
}

void myproc( int )
{
    int FlagType;
}

Wenn Sie einen lokalen Bereichsbezeichner mit demselben Namen wie ein typedef, oder wenn Sie ein Element einer Struktur oder Vereinigung im selben Bereich oder in einem inneren Bereich deklarieren, muss der Typbezeichner angegeben werden. Beispiel:

typedef char FlagType;
const FlagType x;

Um den FlagType-Namen für einen Bezeichner, einen Strukturmember oder einen Unionsmember wiederzuverwenden, muss der Typ angegeben werden:

const int FlagType;  // Type specifier required

Folgendes ist nicht ausreichend:

const FlagType;      // Incomplete specification

da der FlagType Teil des Typs ist, nicht ein Bezeichner, der neu deklariert wird. Diese Erklärung wird als unzulässige Erklärung angenommen, ähnlich wie:

int;  // Illegal declaration

Sie können einen beliebigen Typ mit typedef deklarieren, einschließlich Zeiger-, Funktions- und Arraytypen. Sie können einen typedef-Namen für einen Zeiger auf einen Struktur- oder einen Union-Typ deklarieren, bevor Sie den Struktur- oder Union-Typ definieren, solange die Definition die gleiche Sichtbarkeit wie die Deklaration aufweist.

Beispiele

Eine Verwendung von typedef Deklarationen besteht darin, Deklarationen einheitlicher und kompakter zu gestalten. Beispiel:

typedef char CHAR;          // Character type.
typedef CHAR * PSTR;        // Pointer to a string (char *).
PSTR strchr( PSTR source, CHAR target );
typedef unsigned long ulong;
ulong ul;     // Equivalent to "unsigned long ul;"

typedef Um grundlegende und abgeleitete Typen in derselben Deklaration anzugeben, können Sie Deklaratoren durch Kommas trennen. Beispiel:

typedef char CHAR, *PSTR;

Im folgenden Beispiel wird der Typ DRAWF für eine Funktion bereitgestellt, die keinen Wert zurückgibt und zwei int-Argumente akzeptiert:

typedef void DRAWF( int, int );

Nach der obigen typedef Anweisung, die Deklaration

DRAWF box;

dieser Deklaration:

void box( int, int );

typedef wird häufig mit struct dem Deklarieren und Benennen von benutzerdefinierten Typen kombiniert:

// typedef_specifier2.cpp
#include <stdio.h>

typedef struct mystructtag
{
    int   i;
    double f;
} mystruct;

int main()
{
    mystruct ms;
    ms.i = 10;
    ms.f = 0.99;
    printf_s("%d   %f\n", ms.i, ms.f);
}
10   0.990000

Neudeklarierung von TypeDefs

Die typedef Deklaration kann verwendet werden, um denselben Namen neu einzuschließen, um auf denselben Typ zu verweisen. Beispiel:

Quelldatei file1.h:

// file1.h
typedef char CHAR;

Quelldatei file2.h:

// file2.h
typedef char CHAR;

Quelldatei prog.cpp:

// prog.cpp
#include "file1.h"
#include "file2.h"   // OK

Die Datei prog.cpp enthält zwei Headerdateien, die beide Deklarationen für den Namen CHARenthaltentypedef. Solange die beiden Deklarationen auf denselben Typ verweisen, ist eine solche Neudeklaration zulässig.

Ein typedef Name kann nicht neu definiert werden, der zuvor als anderer Typ deklariert wurde. Erwägen Sie diese Alternative file2.h:

// file2.h
typedef int CHAR;     // Error

Der Compiler gibt einen Fehler prog.cpp aufgrund des Versuchs aus, den Namen CHAR neu zu deklarieren, um auf einen anderen Typ zu verweisen. Diese Richtlinie erstreckt sich auf Konstrukte wie:

typedef char CHAR;
typedef CHAR CHAR;      // OK: redeclared as same type

typedef union REGS      // OK: name REGS redeclared
{                       //  by typedef name with the
    struct wordregs x;  //  same meaning.
    struct byteregs h;
} REGS;

typedefs in C++ vs. C

Die Verwendung des typedef Bezeichners mit Klassentypen wird größtenteils aufgrund der ANSI C-Praxis unterstützt, nicht benannte Strukturen in typedef Deklarationen zu deklarieren. Viele C-Programmierer verwenden z. B. den folgenden Idiom:

// typedef_with_class_types1.cpp
// compile with: /c
typedef struct {   // Declare an unnamed structure and give it the
                   // typedef name POINT.
   unsigned x;
   unsigned y;
} POINT;

Der Vorteil einer solchen Deklaration ist, das Deklarationen wie folgt ermöglicht werden:

POINT ptOrigin;

Anstelle von:

struct point_t ptOrigin;

In C++ ist der Unterschied zwischen typedef Namen und realen Typen (deklariert mit den class, struct, unionund enum Schlüsselwort (keyword)s) eindeutiger. Obwohl die C-Praxis, eine namenslose Struktur in einer typedef Anweisung zu deklarieren, weiterhin funktioniert, bietet sie keine notationalen Vorteile wie in C.

// typedef_with_class_types2.cpp
// compile with: /c /W1
typedef struct {
   int POINT();
   unsigned x;
   unsigned y;
} POINT;

Im vorherigen Beispiel wird eine Klasse mit der Syntax der nicht benannten Klasse typedef deklariertPOINT. POINT wird als Klassenname behandelt. Es gelten jedoch folgende Einschränkungen für Namen, die auf diese Weise eingeführt werden:

  • Der Name (das Synonym) kann nach einem class, structoder union präfix nicht angezeigt werden.

  • Der Name kann nicht als Konstruktor- oder Destruktorname innerhalb einer Klassendeklaration verwendet werden.

Zusammenfassend bietet diese Syntax keinen Mechanismus für Vererbung, Konstruktion oder Zerstörung.