Aliasy i definicje typów (C++)
Możesz użyć deklaracji aliasu, aby zadeklarować nazwę do użycia jako synonim dla wcześniej zadeklarowanego typu. (Ten mechanizm jest również nazywany nieformalnie aliasem typu). Za pomocą tego mechanizmu można również utworzyć szablon aliasu, który może być przydatny w przypadku niestandardowych alokatorów.
Składnia
using identifier = type;
Uwagi
Identyfikator
Nazwa aliasu.
type
Identyfikator typu, dla którego tworzysz alias.
Alias nie wprowadza nowego typu i nie może zmienić znaczenia istniejącej nazwy typu.
Najprostsza forma aliasu jest odpowiednikiem typedef
mechanizmu z języka C++03:
// C++11
using counter = long;
// C++03 equivalent:
// typedef long counter;
Obie te formularze umożliwiają tworzenie zmiennych typu counter
. Coś bardziej przydatnego może być alias typu, taki jak ten dla 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);
Aliasy działają również ze wskaźnikami funkcji, ale są znacznie bardziej czytelne niż równoważna definicja typu:
// 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;
Ograniczenie typedef
mechanizmu polega na tym, że nie działa z szablonami. Jednak składnia aliasu typu w języku C++11 umożliwia tworzenie szablonów aliasów:
template<typename T> using ptr = T*;
// the name 'ptr<T>' is now an alias for pointer to T
ptr<int> ptr_int;
Przykład
W poniższym przykładzie pokazano, jak używać szablonu aliasu z niestandardowym alokatorem — w tym przypadku typem wektora całkowitego. Możesz zastąpić dowolny typ , int
aby utworzyć wygodny alias, aby ukryć złożone listy parametrów w głównym kodzie funkcjonalnym. Korzystając z niestandardowego alokatora w całym kodzie, możesz zwiększyć czytelność i zmniejszyć ryzyko wprowadzenia usterek spowodowanych przez literówki.
#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
Deklaracja typedef
wprowadza nazwę, która w zakresie staje się synonimem typu podanego przez część deklaracji typu deklaracji.
Za pomocą deklaracji typedef można tworzyć krótsze lub bardziej znaczące nazwy typów zdefiniowanych już przez język lub dla zadeklarowanych typów. Nazwy zdefiniowane przez typedef pozwalają na hermetyzację szczegółów implementacji, które mogą się zmieniać.
W przeciwieństwie do class
deklaracji , struct
, union
i enum
deklaracje typedef
nie wprowadzają nowych typów; wprowadzają nowe nazwy istniejących typów.
Nazwy zadeklarowane przy użyciu typedef
zajmują tę samą przestrzeń nazw co inne identyfikatory (z wyjątkiem etykiet instrukcji). W związku z tym nie mogą używać tego samego identyfikatora co wcześniej zadeklarowana nazwa, z wyjątkiem deklaracji typu klasy. Rozważmy następujący przykład:
// typedef_names1.cpp
// C2377 expected
typedef unsigned long UL; // Declare a typedef name, UL.
int UL; // C2377: redefined.
Reguły ukrywania nazw odnoszące się do innych identyfikatorów określają również widoczność nazw zadeklarowanych przy użyciu polecenia typedef
. W związku z tym poniższy przykład jest legalny w języku C++:
// 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
Inne wystąpienie ukrywania nazwy:
// typedef_specifier1.cpp
typedef char FlagType;
int main()
{
}
void myproc( int )
{
int FlagType;
}
W przypadku deklarowania identyfikatora zakresu lokalnego o takiej samej nazwie jak typedef
, lub w przypadku deklarowania elementu członkowskiego struktury lub unii w tym samym zakresie lub w zakresie wewnętrznym należy określić specyfikator typu. Przykład:
typedef char FlagType;
const FlagType x;
Aby ponownie użyć nazwy FlagType
dla identyfikatora, elementu struktury lub unii musi zostać dostarczony typ:
const int FlagType; // Type specifier required
Nie wystarczy powiedzieć
const FlagType; // Incomplete specification
ponieważ element FlagType
jest traktowany jako część typu, a nie identyfikator, który jest ponownie zadeklarowany. Deklaracja ta jest uważana za nielegalną deklarację podobną do:
int; // Illegal declaration
Można zadeklarować dowolny typ za pomocą typedef
polecenia , w tym wskaźnika, funkcji i typów tablic. Można zadeklarować nazwę typedef wskaźnika na strukturę lub unię przed zdefiniowaniem struktury lub unii, tak długo jak definicja ma taką samą widoczność jak deklaracja.
Przykłady
Jednym z zastosowań deklaracji typedef
jest uczynienie deklaracji bardziej jednolitymi i kompaktowymi. Przykład:
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;"
Aby użyć typedef
do określenia podstawowych i pochodnych typów w tej samej deklaracji, można oddzielić deklaratory przecinkami. Przykład:
typedef char CHAR, *PSTR;
W następującym przykładzie podano typ DRAWF
dla funkcji niezwracającej wartości i przyjmującej dwa argumenty typu int:
typedef void DRAWF( int, int );
Po powyższej typedef
instrukcji deklaracja
DRAWF box;
byłaby równoważna z deklaracją
void box( int, int );
typedef
jest często łączone z elementem struct
w celu deklarowania i nazywania typów zdefiniowanych przez użytkownika:
// 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
Ponowne deklarowanie definicji typów
Deklarację typedef
można użyć do ponownego zgłoszenia tej samej nazwy, aby odwoływać się do tego samego typu. Przykład:
Plik file1.h
źródłowy:
// file1.h
typedef char CHAR;
Plik file2.h
źródłowy:
// file2.h
typedef char CHAR;
Plik prog.cpp
źródłowy:
// prog.cpp
#include "file1.h"
#include "file2.h" // OK
prog.cpp
Plik zawiera dwa pliki nagłówkowe, z których oba zawierają typedef
deklaracje dla nazwy CHAR
. Tak długo, jak obie deklaracje odnoszą się do tego samego typu, taka ponowna deklaracja jest akceptowalna.
Nie typedef
można ponownie zdefiniować nazwy, która została wcześniej zadeklarowana jako inny typ. Rozważmy tę alternatywę file2.h
:
// file2.h
typedef int CHAR; // Error
Kompilator zgłasza błąd z prog.cpp
powodu próby ponownego zadeklarowania nazwy CHAR
w celu odwoływania się do innego typu. Te zasady rozszerzają się na konstrukcje, takie jak:
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;
definicje typów w języku C++ a C
Użycie specyfikatora typedef
z typami klas jest obsługiwane w dużej mierze z powodu praktyki ANSI C deklarowania nienazwanych struktur w typedef
deklaracjach. Na przykład wielu programistów języka C używa następującego idiomu:
// 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;
Zaletą takiej deklaracji jest to, że umożliwia deklaracje takie jak:
POINT ptOrigin;
Zamiast:
struct point_t ptOrigin;
W języku C++różnica między nazwami i typami typedef
rzeczywistymi (zadeklarowanymi za pomocą class
słów kluczowych , struct
, union
i enum
) jest bardziej odrębna. Mimo że praktyka języka C deklarowania struktury bez nazw w typedef
instrukcji nadal działa, nie zapewnia żadnych korzyści notacyjnych, jak to ma miejsce w języku C.
// typedef_with_class_types2.cpp
// compile with: /c /W1
typedef struct {
int POINT();
unsigned x;
unsigned y;
} POINT;
W poprzednim przykładzie zadeklarowano klasę o nazwie POINT
przy użyciu nienazwanej składni klasy typedef
. POINT
jest traktowana jako nazwa klasy; jednak następujące ograniczenia dotyczą nazw wprowadzonych w ten sposób:
Nazwa (synonim) nie może być wyświetlana po prefiksie
class
,struct
lubunion
.Nazwa nie może być używana jako konstruktor lub nazwa destruktora w deklaracji klasy.
Podsumowując, ta składnia nie zapewnia żadnego mechanizmu dziedziczenia, budowy ani zniszczenia.
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla