Chcę zdefiniować stałą w C ++, aby była widoczna w kilku plikach źródłowych. Mogę sobie wyobrazić następujące sposoby zdefiniowania tego w pliku nagłówkowym:
#define GLOBAL_CONST_VAR 0xFF
int GLOBAL_CONST_VAR = 0xFF;
- Niektóre funkcje zachowujące wartość (np.
int get_GLOBAL_CONST_VAR()
) enum { GLOBAL_CONST_VAR = 0xFF; }
const int GLOBAL_CONST_VAR = 0xFF;
extern const int GLOBAL_CONST_VAR;
iw jednym pliku źródłowymconst int GLOBAL_CONST_VAR = 0xFF;
Opcja (1) - zdecydowanie nie jest opcją, z której chciałbyś skorzystać
Opcja (2) - zdefiniowanie wystąpienia zmiennej w każdym pliku obiektowym za pomocą pliku nagłówkowego
Opcja (3) - IMO w większości przypadków zabija zbyt dużo
Opcja (4) - w wielu przypadkach może nie być dobra, ponieważ enum nie ma konkretnego typu (C ++ 0X doda możliwość zdefiniowania typu)
Dlatego w większości przypadków muszę wybrać między (5) a (6). Moje pytania:
- Co wolisz (5) czy (6)?
- Dlaczego (5) jest w porządku, a (2) nie?
Odpowiedzi:
(5) mówi dokładnie to, co chcesz powiedzieć. Dodatkowo pozwala kompilatorowi na optymalizację przez większość czasu. (6) z drugiej strony nie pozwoli kompilatorowi kiedykolwiek go zoptymalizować, ponieważ kompilator nie wie, czy w końcu go zmienisz, czy nie.
źródło
extern const int ...
iconst int ...
czy oba są stałe, czyż nie?Zdecydowanie wybierz opcję 5 - jest bezpieczna dla typu i pozwala kompilatorowi na optymalizację (nie pobieraj adresu tej zmiennej :) Również jeśli jest w nagłówku - umieść ją w przestrzeni nazw, aby uniknąć zanieczyszczenia globalnego zasięgu:
// header.hpp namespace constants { const int GLOBAL_CONST_VAR = 0xFF; // ... other related constants } // namespace constants // source.cpp - use it #include <header.hpp> int value = constants::GLOBAL_CONST_VAR;
źródło
header.hpp
do kilku plików źródłowych pojawia się błąd redefinicji .constexpr
i wpisujemy wyliczenia na takie rzeczy.(5) jest „lepsze” niż (6), ponieważ definiuje się
GLOBAL_CONST_VAR
jako Całkowe Wyrażenie Stałe (ICE) we wszystkich jednostkach translacyjnych. Na przykład, będziesz mógł używać go jako rozmiaru tablicy i jako etykiety przypadku we wszystkich jednostkach tłumaczeniowych. W przypadku (6)GLOBAL_CONST_VAR
będzie ICE tylko w tej jednostce tłumaczeniowej, w której jest zdefiniowany i tylko po punkcie definicji. W innych jednostkach tłumaczeniowych nie będzie działać jako ICE.Należy jednak pamiętać, że (5) zapewnia
GLOBAL_CONST_VAR
powiązanie wewnętrzne, co oznacza, że „tożsamość adresu”GLOBAL_CONST_VAR
będzie inna w każdej jednostce tłumaczeniowej, tj.&GLOBAL_CONST_VAR
Poda inną wartość wskaźnika w każdej jednostce tłumaczeniowej. W większości przypadków nie ma to znaczenia, ale jeśli potrzebujesz stałego obiektu, który ma spójną globalną „tożsamość adresową”, musiałbyś iść z (6), poświęcając ICE-ność stałej w proces.Również, gdy ICE-ność stałej nie jest problemem (nie jest to typ całkowy), a rozmiar typu rośnie (nie jest to typ skalarny), wtedy (6) zwykle staje się lepszym podejściem niż (5).
(2) nie jest OK, ponieważ
GLOBAL_CONST_VAR
in (2) ma domyślnie łącze zewnętrzne. Jeśli umieścisz go w pliku nagłówkowym, zwykle otrzymasz wiele definicjiGLOBAL_CONST_VAR
, co jest błędem.const
obiekty w C ++ mają domyślnie wewnętrzne powiązanie, dlatego (5) działa (i dlatego, jak powiedziałem powyżej, otrzymujesz oddzielną, niezależnąGLOBAL_CONST_VAR
w każdej jednostce tłumaczeniowej).Począwszy od C ++ 17 masz możliwość zadeklarowania
inline extern const int GLOBAL_CONST_VAR = 0xFF;
w pliku nagłówkowym. Daje to ICE we wszystkich jednostkach tłumaczeniowych (podobnie jak metoda (5)), jednocześnie zachowując globalną tożsamość adresową
GLOBAL_CONST_VAR
- we wszystkich jednostkach tłumaczeniowych będzie miał ten sam adres.źródło
Jeśli używasz C ++ 11 lub nowszego, spróbuj użyć stałych czasu kompilacji:
constexpr int GLOBAL_CONST_VAR{ 0xff };
źródło
Jeśli ma to być stała, to oznacz ją jako stałą - dlatego moim zdaniem 2 jest złe.
Kompilator może użyć stałej natury wartości, aby rozwinąć niektóre matematyki, a nawet inne operacje, które używają tej wartości.
Wybór między 5 a 6 - hmm; Po prostu czuję się lepiej dla mnie.
W 6) wartość jest niepotrzebnie oddzielana od deklaracji.
Zwykle miałbym jeden lub więcej takich nagłówków, które definiują w nich tylko stałe itp., A następnie żadnych innych „sprytnych” rzeczy - ładne, lekkie nagłówki, które można łatwo umieścić w dowolnym miejscu.
źródło
Aby odpowiedzieć na drugie pytanie:
(2) jest niezgodne z prawem, ponieważ narusza zasadę jednej definicji. Określa
GLOBAL_CONST_VAR
w każdym pliku, w którym jest zawarty, tj. Więcej niż raz. (5) jest legalny, ponieważ nie podlega zasadzie jednej definicji. KażdaGLOBAL_CONST_VAR
jest oddzielną definicją lokalną dla tego pliku, w którym jest zawarta. Wszystkie te definicje mają oczywiście tę samą nazwę i wartość, ale ich adresy mogą się różnić.źródło
inline
Zmienne C ++ 17Ta niesamowita funkcja C ++ 17 pozwala nam:
constexpr
: Jak zadeklarować extern constexpr?main.cpp
#include <cassert> #include "notmain.hpp" int main() { // Both files see the same memory address. assert(¬main_i == notmain_func()); assert(notmain_i == 42); }
notmain.hpp
#ifndef NOTMAIN_HPP #define NOTMAIN_HPP inline constexpr int notmain_i = 42; const int* notmain_func(); #endif
notmain.cpp
#include "notmain.hpp" const int* notmain_func() { return ¬main_i; }
Skompiluj i uruchom:
g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o ./main
GitHub upstream .
Zobacz także: Jak działają zmienne wbudowane?
Standard C ++ dotyczący zmiennych wbudowanych
Standard C ++ gwarantuje, że adresy będą takie same. C ++ 17 N4659 standardowa wersja robocza 10.1.6 „Specyfikator wbudowany”:
cppreference https://en.cppreference.com/w/cpp/language/inline wyjaśnia, że jeśli
static
nie jest podane, ma link zewnętrzny.Implementacja zmiennych inline
Możemy obserwować, jak jest realizowany za pomocą:
który zawiera:
main.o: U _GLOBAL_OFFSET_TABLE_ U _Z12notmain_funcv 0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__ U __assert_fail 0000000000000000 T main 0000000000000000 u notmain_i notmain.o: 0000000000000000 T _Z12notmain_funcv 0000000000000000 u notmain_i
i
man nm
mówi ou
:więc widzimy, że jest do tego dedykowane rozszerzenie ELF.
Testowane na GCC 7.4.0, Ubuntu 18.04.
źródło
const int GLOBAL_CONST_VAR = 0xFF;
ponieważ jest stała!
źródło
To zależy od twoich wymagań. (5) jest najlepszy do większości normalnych zastosowań, ale często powoduje ciągłe zajmowanie miejsca w każdym pliku obiektowym. (6) mogą obejść ten problem w sytuacjach, w których jest to ważne.
(4) jest również dobrym wyborem, jeśli Twoim priorytetem jest zagwarantowanie, że przestrzeń dyskowa nigdy nie zostanie przydzielona, ale działa tylko dla stałych całkowitych.
źródło
#define GLOBAL_CONST_VAR 0xFF // this is C code not C++ int GLOBAL_CONST_VAR = 0xFF; // it is not constant and maybe not compilled Some function returing the value (e.g. int get_LOBAL_CONST_VAR()) // maybe but exists better desision enum { LOBAL_CONST_VAR = 0xFF; } // not needed, endeed, for only one constant (enum elms is a simple int, but with secial enumeration) const int GLOBAL_CONST_VAR = 0xFF; // it is the best extern const int GLOBAL_CONST_VAR; //some compiller doesn't understand this
źródło