Jaki jest najlepszy sposób na zainicjowanie prywatnego, statycznego elementu danych w C ++? Próbowałem tego w moim pliku nagłówkowym, ale daje mi to dziwne błędy linkera:
class foo
{
private:
static int i;
};
int foo::i = 0;
Zgaduję, że dzieje się tak, ponieważ nie mogę zainicjować prywatnego członka spoza klasy. Więc jaki jest najlepszy sposób, aby to zrobić?
c++
initialization
static-members
Jason Baker
źródło
źródło
inline static int x[] = {1, 2, 3};
. Zobacz en.cppreference.com/w/cpp/language/static#Static_data_membersOdpowiedzi:
Deklaracja klasy powinna znajdować się w pliku nagłówkowym (lub w pliku źródłowym, jeśli nie jest udostępniony).
Plik: foo.h
Ale inicjalizacja powinna być w pliku źródłowym.
Plik: foo.cpp
Jeśli inicjalizacja znajduje się w pliku nagłówkowym, wówczas każdy plik zawierający plik nagłówkowy będzie miał definicję elementu statycznego. Dlatego w fazie łączenia wystąpią błędy linkera, ponieważ kod inicjujący zmienną zostanie zdefiniowany w wielu plikach źródłowych. Inicjalizację
static int i
należy wykonać poza jakąkolwiek funkcją.Uwaga: Matt Curtis: Zwraca uwagę, że C ++ umożliwia uproszczenie powyżej jeśli zmienna statyczna jest członkiem const int typu (np
int
,bool
,char
). Następnie możesz zadeklarować i zainicjować zmienną składową bezpośrednio w deklaracji klasy w pliku nagłówkowym:źródło
Dla zmiennej :
foo.h:
foo.cpp:
Wynika to z faktu, że
foo::i
w twoim programie może znajdować się tylko jedna instancja . Jest to coś w rodzaju odpowiednikaextern int i
w pliku nagłówkowym iint i
pliku źródłowym.Dla stałej możesz umieścić wartość prosto w deklaracji klasy:
źródło
private
zmienne mogą być tutaj inicjowane poza klasą, czy można to zrobić również dla zmiennych niestatycznych.Class
nie ma żadnego sensu w Cpp.Od C ++ 17 elementy statyczne można definiować w nagłówku za pomocą słowa kluczowego inline .
http://en.cppreference.com/w/cpp/language/static
„Statyczny element danych może zostać zadeklarowany jako wbudowany. Wbudowany element danych statycznych może być zdefiniowany w definicji klasy i może określać domyślny inicjator elementu. Nie wymaga definicji poza klasą:”
źródło
Dla przyszłych widzów tego pytania chcę zauważyć , że powinieneś unikać sugestii monkey0506 .
Pliki nagłówkowe służą do deklaracji.
Pliki nagłówkowe są kompilowane raz dla każdego
.cpp
pliku, który bezpośrednio lub pośrednio#includes
, a kod poza jakąkolwiek funkcją jest uruchamiany przy inicjalizacji programu, wcześniejmain()
.Umieszczając:
foo::i = VALUE;
w nagłówku,foo:i
zostanie przypisana wartośćVALUE
(cokolwiek to jest) dla każdego.cpp
pliku, a przypisania te będą wykonywane w nieokreślonej kolejności (określonej przez linker) przedmain()
uruchomieniem.Co jeśli będziemy
#define VALUE
mieć inną liczbę w jednym z naszych.cpp
plików? Skompiluje się dobrze i nie będziemy mogli wiedzieć, kto wygra, dopóki nie uruchomimy programu.Nigdy umieścić kod wykonywany w nagłówku tego samego powodu, że nigdy
#include
w.cpp
pliku.uwzględnij strażników (które, jak się zgadzam, zawsze powinieneś używać) chronią cię przed czymś innym: ten sam nagłówek jest pośrednio
#include
d wiele razy podczas kompilacji jednego.cpp
plikuźródło
W kompilatorze Microsoft [1] zmienne statyczne, które nie są
int
podobne, można również zdefiniować w pliku nagłówkowym, ale poza deklaracją klasy, przy użyciu specyfikacji Microsoft__declspec(selectany)
.Zauważ, że nie mówię, że to jest dobre, po prostu mówię, że można to zrobić.
[1] Obecnie więcej kompilatorów niż wsparcie MSC
__declspec(selectany)
- przynajmniej gcc i clang. Może nawet więcej.źródło
Jest poprawną składnią do inicjowania zmiennej, ale musi ona znajdować się w pliku źródłowym (.cpp), a nie w nagłówku.
Ponieważ jest to zmienna statyczna, kompilator musi utworzyć tylko jedną jej kopię. Musisz mieć wiersz „int foo: i” gdzieś w kodzie, aby poinformować kompilator, gdzie go umieścić, w przeciwnym razie pojawi się błąd łącza. Jeśli znajduje się w nagłówku, otrzymasz kopię w każdym pliku, który zawiera nagłówek, więc otrzymaj wielokrotnie zdefiniowane błędy symboli od linkera.
źródło
Nie mam tutaj wystarczającej liczby przedstawicieli, aby dodać to jako komentarz, ale IMO to dobry styl, aby pisać nagłówki za pomocą #include guards , co, jak zauważył Paranaix kilka godzin temu, zapobiegłoby błędowi wielu definicji. O ile nie używasz już osobnego pliku CPP, nie trzeba go używać do inicjowania statycznych niezintegrowanych elementów.
Nie widzę potrzeby używania do tego osobnego pliku CPP. Jasne, że tak, ale nie ma technicznego powodu, dla którego powinieneś.
źródło
#endif // FOO_H
#pragma once
Jeśli chcesz zainicjować jakiś typ złożony (ciąg znaków), możesz zrobić coś takiego:
Ponieważ
ListInitializationGuard
wSomeClass::getList()
metodzie jest zmienna statyczna , zostanie zbudowana tylko raz, co oznacza, że konstruktor jest wywoływany raz. Będzie toinitialize _list
zmienna wartości trzeba. Każde kolejne wywołaniegetList
zwróci już zainicjowany_list
obiekt.Oczywiście musisz
_list
zawsze uzyskiwać dostęp do obiektu, wywołującgetList()
metodę.źródło
C ++ 11 wzorzec statycznego konstruktora, który działa dla wielu obiektów
Jeden idiom zaproponowano na stronie : https://stackoverflow.com/a/27088552/895245, ale tutaj pojawia się czystsza wersja, która nie wymaga tworzenia nowej metody dla członka.
main.cpp
GitHub w górę .
Skompiluj i uruchom:
Zobacz także: statyczne konstruktory w C ++? Muszę zainicjować prywatne obiekty statyczne
Testowane na Ubuntu 19.04.
Zmienna wbudowana C ++ 17
Wspomniano na: https://stackoverflow.com/a/45062055/895245, ale oto przykład, który można uruchomić na wielu plikach, aby uczynić go jeszcze bardziej zrozumiałym: Jak działają zmienne wbudowane?
źródło
Możesz także dołączyć przypisanie do pliku nagłówka, jeśli używasz osłon nagłówka. Użyłem tej techniki do utworzonej przeze mnie biblioteki C ++. Innym sposobem osiągnięcia tego samego wyniku jest zastosowanie metod statycznych. Na przykład...
Powyższy kod ma „bonus” polegający na tym, że nie wymaga pliku CPP / źródłowego. Znów metoda, której używam dla moich bibliotek C ++.
źródło
Podążam za pomysłem Karla. Podoba mi się i teraz też go używam. Zmieniłem nieco notację i dodałem trochę funkcjonalności
to wychodzi
źródło
Działa również w pliku privateStatic.cpp:
źródło
Co z
set_default()
metodą?Musielibyśmy tylko użyć tej
set_default(int x)
metody, a naszastatic
zmienna zostałaby zainicjowana.Nie byłoby to niezgodne z pozostałymi komentarzami, w rzeczywistości jest zgodne z tą samą zasadą inicjowania zmiennej w zakresie globalnym, ale za pomocą tej metody wyrażamy ją jasno (i łatwo ją zrozumieć) zamiast definicji wiszącej tam zmiennej.
źródło
Napotkany problem z linkerem jest prawdopodobnie spowodowany:
Jest to powszechny problem dla tych, którzy zaczynają od C ++. Członek klasy statycznej musi być zainicjowany w pojedynczej jednostce tłumaczenia, tj. W jednym pliku źródłowym.
Niestety element klasy statycznej musi zostać zainicjowany poza ciałem klasy. To komplikuje pisanie kodu zawierającego tylko nagłówek, dlatego używam zupełnie innego podejścia. Możesz dostarczyć swój obiekt statyczny za pomocą statycznej lub niestatycznej funkcji klasy, na przykład:
źródło
Jednym ze „oldschoolowych” sposobów definiowania stałych jest zastąpienie ich
enum
:W ten sposób nie wymaga opatrzenia definicji i unika podejmowania stałej lwartość , które można zaoszczędzić trochę głowy, na przykład, gdy przypadkowo ODR wykorzystanie go.
źródło
Chciałem tylko wspomnieć o czymś trochę dziwnym, kiedy po raz pierwszy to spotkałem.
Musiałem zainicjować prywatnego członka danych statycznych w klasie szablonów.
w .h lub .hpp wygląda to tak, aby zainicjować element danych statycznych klasy szablonu:
źródło
Czy to służy twojemu celowi?
źródło