Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Pojęcie typu jest ważne w języku C++. Zwracana wartość każdej zmiennej, argumentu funkcji oraz funkcji musi mieć typ, aby mogła zostać skompilowana. Ponadto wszystkie wyrażenia (w tym wartości literału) są niejawnie podane typowi przez kompilator przed ich oceną. Niektóre przykłady typów obejmują wbudowane typy, takie jak int
przechowywanie wartości całkowitych, double
przechowywanie wartości zmiennoprzecinkowych lub standardowych typów bibliotek, takich jak klasa std::basic_string
do przechowywania tekstu. Możesz utworzyć własny typ, definiując element class
lub struct
. Typ określa ilość pamięci przydzielonej dla zmiennej (lub wynik wyrażenia). Typ określa również rodzaje wartości, które mogą być przechowywane, jak kompilator interpretuje wzorce bitowe w tych wartościach i operacje, które można na nich wykonać. Ten artykuł zawiera nieformalny przegląd głównych funkcji systemu typów C++.
Terminologia
Typ skalarny: typ, który zawiera pojedynczą wartość zdefiniowanego zakresu. Skalarne obejmują typy arytmetyczne (wartości całkowite lub zmiennoprzecinkowe), składowe typu wyliczenia, typy wskaźników, typy wskaźników do składowych i std::nullptr_t
. Typy podstawowe są zwykle typami skalarnymi.
Typ złożony: typ, który nie jest typem skalarnym. Typy złożone obejmują typy tablic, typy funkcji, typy klas (lub struktury), typy unii, wyliczenia, odwołania i wskaźniki do niestacjonalnych składowych klas.
Zmienna: symboliczna nazwa ilości danych. Nazwa może służyć do uzyskiwania dostępu do danych, do których odnosi się w całym zakresie kodu, w którym jest definiowany. W języku C++zmienna jest często używana do odwoływania się do wystąpień typów danych skalarnych, natomiast wystąpienia innych typów są zwykle nazywane obiektami.
Obiekt: Dla uproszczenia i spójności w tym artykule użyto terminu object w celu odwoływania się do dowolnego wystąpienia klasy lub struktury. Gdy jest on używany w ogólnym sensie, zawiera wszystkie typy, nawet zmienne skalarne.
Typ zasobnika (zwykłe stare dane): ta nieformalna kategoria typów danych w języku C++ odnosi się do typów skalarnych (zobacz sekcję Typy podstawowe) lub klasy POD. Klasa POD nie ma statycznych składowych danych, które nie są również identyfikatorami POD i nie ma konstruktorów zdefiniowanych przez użytkownika, destruktorów zdefiniowanych przez użytkownika ani operatorów przypisania zdefiniowanych przez użytkownika. Klasa POD nie ma też żadnych funkcji wirtualnych, nie ma klasy podstawowej ani żadnych prywatnych lub chronionych niestatycznych składowych danych. Typy POD są często używane do wymiany danych zewnętrznych, na przykład przy użyciu modułu napisanego w języku C (który ma tylko typy POD).
Określanie typów zmiennych i funkcji
Język C++ jest językiem silnie typizowanego i statycznie typizowanego języka; każdy obiekt ma typ i ten typ nigdy się nie zmienia. W przypadku deklarowania zmiennej w kodzie należy jawnie określić jej typ lub użyć auto
słowa kluczowego , aby wywoływać typ z inicjatora. Podczas deklarowania funkcji w kodzie należy określić typ jego zwracanej wartości i każdego argumentu. Użyj zwracanego typu void
wartości, jeśli żadna wartość nie jest zwracana przez funkcję. Wyjątek dotyczy używania szablonów funkcji, które umożliwiają używanie argumentów dowolnego typu.
Po pierwszym zadeklarowaniu zmiennej nie można zmienić jej typu w pewnym późniejszym momencie. Można jednak skopiować wartość zmiennej lub wartość zwracaną funkcji do innej zmiennej innego typu. Takie operacje są nazywane konwersjami typów, które czasami są niezbędne, ale są również potencjalnymi źródłami utraty lub niepoprawności danych.
Podczas deklarowania zmiennej typu ZASOB zdecydowanie zalecamy jego zainicjowanie , co oznacza nadanie jej wartości początkowej. Dopóki zmienna nie zostanie zainicjalizowana, ma ona wartość „śmieciową”, składającą się z bitów, które poprzednio znajdowały się w tej lokalizacji pamięci. Jest to ważny aspekt języka C++, który należy pamiętać, zwłaszcza jeśli pochodzisz z innego języka, który obsługuje inicjację. W przypadku deklarowania zmiennej typu klasy innej niż POD konstruktor obsługuje inicjowanie.
Poniższy przykład pokazuje kilka prostych deklaracji zmiennych z opisami każdej z nich. W przykładzie pokazano również, jak kompilator używa informacji o typie, aby umożliwiać lub uniemożliwiać pewne kolejne operacje na zmiennej.
int result = 0; // Declare and initialize an integer.
double coefficient = 10.8; // Declare and initialize a floating
// point value.
auto name = "Lady G."; // Declare a variable and let compiler
// deduce the type.
auto address; // error. Compiler cannot deduce a type
// without an intializing value.
age = 12; // error. Variable declaration must
// specify a type or use auto!
result = "Kenny G."; // error. Can't assign text to an int.
string result = "zero"; // error. Can't redefine a variable with
// new type.
int maxValue; // Not recommended! maxValue contains
// garbage bits until it is initialized.
Podstawowe (wbudowane) typy
W odróżnieniu do innych języków, C++ nie ma uniwersalnego typu bazowego, z którego wywodzą się wszystkie inne typy. Język zawiera wiele podstawowych typów, nazywanych również typami wbudowanymi. Te typy obejmują typy liczbowe, takie jak int
, , double
long
, , bool
oraz char
wchar_t
typy dla znaków ASCII i UNICODE, odpowiednio. Większość całkowitych typów podstawowych (z wyjątkiem bool
typów , double
, wchar_t
i powiązanych) ma unsigned
wszystkie wersje, które modyfikują zakres wartości, które może przechowywać zmienna. Na przykład , int
który przechowuje 32-bitową liczbę całkowitą ze znakiem, może reprezentować wartość z -2,147,483,648 do 2,147,483,647. Element unsigned int
, który jest również przechowywany jako 32 bity, może przechowywać wartość z zakresu od 0 do 4294 967 295. Całkowita liczba możliwych wartości w każdym przypadku jest taka sama, zmienia się tylko zakres.
Kompilator rozpoznaje te wbudowane typy i ma wbudowane reguły, które określają, jakie operacje można wykonywać na nich, oraz sposób ich konwertowania na inne typy podstawowe. Aby uzyskać pełną listę wbudowanych typów oraz ich rozmiar i limity liczbowe, zobacz Wbudowane typy.
Na poniższej ilustracji przedstawiono względne rozmiary wbudowanych typów w implementacji języka Microsoft C++:
W poniższej tabeli wymieniono najczęściej używane typy podstawowe oraz ich rozmiary w implementacji języka Microsoft C++:
Typ | Rozmiar | Komentarz |
---|---|---|
int |
4 bajty | Wybór domyślny dla wartości całkowitych. |
double |
8 bajtów | Wybór domyślny dla wartości zmiennopozycyjnych. |
bool |
1 bajt | Przedstawia wartości, które mogą być prawdziwe lub fałszywe. |
char |
1 bajt | Używaj dla znaków ASCII w starszych ciągach typu C lub obiektach std::string, które nigdy nie będą musiały zostać skonwertowane na UNICODE. |
wchar_t |
2 bajty | Przedstawia wartości znaku „szerokiego”, które mogą być zakodowane w formacie UNICODE (UTF-16 w systemie Windows, inne systemy operacyjne mogą się różnić).
wchar_t to typ znaku używany w ciągach typu std::wstring . |
unsigned char |
1 bajt | Język C++ nie ma wbudowanego typu bajtów. Użyj unsigned char polecenia , aby reprezentować wartość bajtu. |
unsigned int |
4 bajty | Wybór domyślny dla flag bitowych. |
long long |
8 bajtów | Reprezentuje znacznie większy zakres wartości całkowitych. |
Inne implementacje języka C++ mogą używać różnych rozmiarów dla niektórych typów liczbowych. Aby uzyskać więcej informacji o rozmiarach i relacjach rozmiaru, których wymaga standard C++, zobacz Wbudowane typy.
Typ void
Typ void
jest specjalnym typem; nie można zadeklarować zmiennej typu , ale można zadeklarować zmienną typu void
void *
(wskaźnik do void
), która jest czasami konieczna w przypadku przydzielania nieprzetworzonej (nietypowej) pamięci. Jednak wskaźniki void
nie są bezpieczne dla typu, a ich użycie jest zniechęcane do nowoczesnego języka C++. W deklaracji funkcji zwracana wartość oznacza, void
że funkcja nie zwraca wartości; użycie jej jako typu zwracanego jest typowym i akceptowalnym użyciem void
elementu . Chociaż język C wymaga funkcji, które mają zerowe parametry do zadeklarowania void
na liście parametrów, fn(void)
na przykład , ta praktyka jest odradzana w nowoczesnym języku C++; należy zadeklarować fn()
funkcję bez parametrów . Aby uzyskać więcej informacji, zobacz Konwersje typów i bezpieczeństwo typów.
const
kwalifikator typu
Każdy wbudowany lub zdefiniowany przez użytkownika typ może być kwalifikowany const
przez słowo kluczowe . Ponadto funkcje składowe mogą być const
-kwalifikowane, a nawet const
-przeciążone. Nie można zmodyfikować wartości const
typu po zainicjowaniu.
const double PI = 3.1415;
PI = .75; //Error. Cannot modify const variable.
const
Kwalifikator jest szeroko używany w deklaracjach funkcji i zmiennych, a "const correctness" jest ważną koncepcją w języku C++. Zasadniczo oznacza const
to, że w celu zagwarantowania w czasie kompilacji wartości nie są modyfikowane przypadkowo. Aby uzyskać więcej informacji, zobacz const
.
Typ const
różni się od jego wersji innejconst
niż wersja, const int
na przykład jest odrębnym typem od int
. Operator języka C++ const_cast
można używać w rzadkich przypadkach, gdy musisz usunąć const-ness ze zmiennej. Aby uzyskać więcej informacji, zobacz Konwersje typów i bezpieczeństwo typów.
Typy ciągu
Mówiąc ściśle, język C++ nie ma wbudowanego typu ciągu; char
i wchar_t
przechowuj pojedyncze znaki — należy zadeklarować tablicę tych typów, aby przybliżyć ciąg, dodając wartość null zakończenia (na przykład ASCII '\0'
) do elementu tablicy jeden obok ostatniego prawidłowego znaku (nazywanego również ciągiem w stylu C). Ciągi stylu C wymagały znacznie więcej kodu lub korzystania z funkcji zewnętrznej biblioteki obsługującej ciągi. Jednak w nowoczesnym języku C++mamy standardowe typy std::string
bibliotek (dla ciągów znaków typu 8-bitowego) lub char
(dla 16-bitowych std::wstring
wchar_t
ciągów znaków typu). Te kontenery biblioteki standardowej języka C++ mogą być uważane za natywne typy ciągów, ponieważ są one częścią standardowych bibliotek, które są zawarte w dowolnym zgodnym środowisku kompilacji C++.
#include <string>
Użyj dyrektywy , aby udostępnić te typy w programie. (Jeśli używasz MFC lub ATL, CString
klasa jest również dostępna, ale nie jest częścią standardu C++). Korzystanie z tablic znaków zakończonych wartością null (wcześniej wymienionych ciągów w stylu C) jest odradzane we współczesnym języku C++.
Typy definiowane przez użytkownika
Podczas definiowania class
konstrukcji , , struct
union
lub enum
jest używana w pozostałej części kodu tak, jakby była to podstawowy typ. Zajmuje znany obszar w pamięci, a niektóre zasady sposobu, w jaki można go używać, dotyczą sprawdzania w czasie kompilacji i w trakcie uruchomienia, przez cały okres istnienia programu. Główne różnice między głównymi wbudowanymi typami a typami definiowanymi przez użytkownika są następujące:
Kompilator nie ma wbudowanej wiedzy o typie zdefiniowanym przez użytkownika. Poznaje typ, gdy po raz pierwszy napotka definicję podczas procesu kompilacji.
Ty określasz, jakie operacje mogą zostać przeprowadzone na typie i jak może on zostać przekonwertowany do innych typów, definiując (przez przeciążenie) odpowiednie operatory, albo jako funkcje składowych, albo jako funkcje, które nie są składowymi. Aby uzyskać więcej informacji, zobacz Przeciążenie funkcji
Typy wskaźnika
Podobnie jak w najwcześniejszych wersjach języka C++ nadal umożliwia deklarowanie zmiennej typu wskaźnika przy użyciu specjalnego deklaratora *
(gwiazdki). Typ wskaźnika przechowuje adres lokalizacji w pamięci, gdzie jest przechowywana rzeczywista wartość danych. W nowoczesnym języku C++te typy wskaźników są nazywane nieprzetworzonymi wskaźnikami i są dostępne w kodzie za pomocą operatorów specjalnych: *
(gwiazdka) lub ->
(kreska z większą niż, często nazywaną strzałką). Ta operacja dostępu do pamięci jest nazywana wyłuszczeniem. Który operator, którego używasz, zależy od tego, czy wskaźnik jest wyłuszczany do skalarnego, czy wskaźnika do elementu członkowskiego w obiekcie.
Praca z typami wskaźników od dawna jest jednym z najtrudniejszych i najbardziej dezorientujących aspektów tworzenia programów w C i C++. W tej sekcji opisano niektóre fakty i praktyki ułatwiające korzystanie z pierwotnych wskaźników, jeśli chcesz. Jednak w nowoczesnym języku C++nie jest już wymagane (lub zalecane) używanie nieprzetworzonych wskaźników własności obiektów ze względu na ewolucję inteligentnego wskaźnika (omówionego bardziej na końcu tej sekcji). Nadal przydatne i bezpieczne jest używanie surowych wskaźników do obserwacji obiektów. Jeśli jednak musisz użyć ich do własności obiektu, należy to zrobić ostrożnie i ostrożnie rozważyć sposób tworzenia i niszczenia obiektów należących do nich.
Pierwszą rzeczą, którą należy wiedzieć, jest to, że deklaracja nieprzetworzonej zmiennej wskaźnika przydziela tylko wystarczającą ilość pamięci do przechowywania adresu: lokalizacja pamięci, do której wskaźnik odwołuje się podczas wyłuskania. Deklaracja wskaźnika nie przydziela pamięci potrzebnej do przechowywania wartości danych. (Ta pamięć jest również nazywana magazynem zapasowym). Innymi słowy, deklarując zmienną nieprzetworzonego wskaźnika, tworzysz zmienną adresową pamięci, a nie rzeczywistą zmienną danych. Jeśli wyłuszczenie zmiennej wskaźnika przed upewnieniem się, że zawiera prawidłowy adres magazynu kopii zapasowych, powoduje niezdefiniowane zachowanie (zazwyczaj błąd krytyczny) w programie. Poniższy przykład przedstawia ten rodzaj błędu:
int* pNumber; // Declare a pointer-to-int variable.
*pNumber = 10; // error. Although this may compile, it is
// a serious error. We are dereferencing an
// uninitialized pointer variable with no
// allocated memory to point to.
Przykład wyłuskuje typ wskaźnika, bez przydzielania pamięci na przechowywanie rzeczywistych danych całkowitoliczbowych lub prawidłowego przypisanego adresu pamięci. Poniższy kod poprawia te błędy:
int number = 10; // Declare and initialize a local integer
// variable for data backing store.
int* pNumber = &number; // Declare and initialize a local integer
// pointer variable to a valid memory
// address to that backing store.
...
*pNumber = 41; // Dereference and store a new value in
// the memory pointed to by
// pNumber, the integer variable called
// "number". Note "number" was changed, not
// "pNumber".
Poprawiony przykład kodu używa pamięci stosu lokalnego do utworzenia magazynu zapasowego, do pNumber
którego wskazuje. Dla uproszczenia używamy podstawowego typu. W praktyce magazyny zapasowe wskaźników są najczęściej typami zdefiniowanymi przez użytkownika, które są dynamicznie przydzielane w obszarze pamięci nazywanym stertą (lub ) przy użyciu wyrażenia kluczowego (w programowaniu w stylu C użyto starszej new
funkcji biblioteki środowiska uruchomieniowego języka C). Po przydzieleniu te zmienne są zwykle określane jako obiekty, zwłaszcza jeśli są oparte na definicji klasy. Pamięć przydzielona new
za pomocą polecenia musi zostać usunięta przez odpowiednią delete
instrukcję (lub, jeśli funkcja została użyta malloc()
do jej przydzielenia, funkcja free()
środowiska uruchomieniowego języka C ).
Można jednak łatwo zapomnieć o usunięciu dynamicznie przydzielonego obiektu — zwłaszcza w złożonym kodzie, co powoduje usterkę zasobu nazywaną wyciekiem pamięci. Z tego powodu stosowanie surowych wskaźników jest odradzane we współczesnym języku C++. Prawie zawsze lepiej jest opakowować nieprzetworzone wskaźniki w inteligentnym wskaźniku, który automatycznie zwalnia pamięć po wywołaniu destruktora. (Oznacza to, że gdy kod wykracza poza zakres inteligentnego wskaźnika). Używając inteligentnych wskaźników, praktycznie eliminujesz całą klasę usterek w programach języka C++. W poniższym przykładzie przyjęto MyClass
założenie, że jest typem zdefiniowanym przez użytkownika, który ma metodę publiczną DoSomeWork();
void someFunction() {
unique_ptr<MyClass> pMc(new MyClass);
pMc->DoSomeWork();
}
// No memory leak. Out-of-scope automatically calls the destructor
// for the unique_ptr, freeing the resource.
Aby uzyskać więcej informacji na temat inteligentnych wskaźników, zobacz Inteligentne wskaźniki.
Aby uzyskać więcej informacji na temat konwersji wskaźników, zobacz Konwersje typów i bezpieczeństwo typów.
Aby uzyskać więcej informacji na temat wskaźników ogólnie, zobacz Wskaźniki.
Typy danych Windows
W klasycznym programowaniu Win32 dla języków C i C++większość funkcji używa definicji typów i #define
makr specyficznych dla systemu Windows (zdefiniowanych w programie windef.h
) w celu określenia typów parametrów i zwracanych wartości. Te typy danych systemu Windows są głównie nazwami specjalnymi (aliasami) nadanym wbudowanym typom C/C++. Aby uzyskać pełną listę tych definicji typów i definicji preprocesora, zobacz Typy danych systemu Windows. Niektóre z tych definicji typów, takie jak HRESULT
i LCID
, są przydatne i opisowe. Inne, takie jak INT
, nie mają specjalnego znaczenia i są tylko aliasami dla podstawowych typów języka C++. Inne typy danych systemu Windows mają nazwy pochodzące z czasów programowania w C i procesorów 16-bitowych. Nie mają one żadnego celu ani znaczenia w przypadku nowoczesnego sprzętu lub systemu operacyjnego. Istnieją również specjalne typy danych skojarzone z biblioteką środowisko wykonawcze systemu Windows wymienioną jako środowisko wykonawcze systemu Windows podstawowe typy danych. W nowoczesnym języku C++ogólne wytyczne dotyczą preferowania typów podstawowych języka C++, chyba że typ systemu Windows komunikuje pewne dodatkowe znaczenie dotyczące interpretacji wartości.
Więcej informacji
Aby uzyskać więcej informacji na temat systemu typów języka C++, zobacz następujące artykuły.
Typy wartości
Opisuje typy wartości wraz z problemami dotyczącymi ich użycia.
Konwersje typów i bezpieczeństwo typów
Opisuje typowe problemy przy konwersji typu i pokazuje, jak ich unikać.
Zobacz też
Witamy z powrotem w języku C++
Dokumentacja języka C++
Standardowa biblioteka C++