Czy można zainicjować struktury w C ++, jak wskazano poniżej
struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
address temp_address =
{ .city = "Hamilton", .prov = "Ontario" };
Linki tutaj i tutaj wspominają, że można używać tego stylu tylko w C. Jeśli tak, to dlaczego nie jest to możliwe w C ++? Czy istnieje jakiś podstawowy techniczny powód, dla którego nie jest on zaimplementowany w C ++, czy też jest to zła praktyka, aby używać tego stylu. Lubię używać tego sposobu inicjowania, ponieważ moja struktura jest duża, a ten styl daje mi wyraźną czytelność tego, jaka wartość jest przypisana do którego elementu.
Proszę podzielić się ze mną, jeśli istnieją inne sposoby, dzięki którym możemy osiągnąć taką samą czytelność.
Przed opublikowaniem tego pytania zapoznałem się z poniższymi linkami
Odpowiedzi:
Jeśli chcesz wyjaśnić, co to jest każda wartość inicjalizatora, po prostu podziel ją na wiele wierszy z komentarzem do każdego:
źródło
char*
) jak jeden z pozostałych elementów powyżej lub poniżej w strukturze, ponieważ nie ma ryzyka zamiany.(struct timeval){ .seconds = 0, .microseconds = 100 }
będzie zawsze wynosić sto mikrosekund, aletimeval { 0, 100 }
może wynosić sto SEKUND . Nie chcesz znaleźć czegoś takiego na własnej skórze.Po tym, jak moje pytanie nie przyniosło zadowalającego rezultatu (ponieważ C ++ nie implementuje opartego na tagach init dla struktur), podjąłem sztuczkę, którą tu znalazłem: Czy członkowie struktury C ++ są domyślnie inicjowani na 0?
Dla ciebie oznaczałoby to zrobienie tego:
Z pewnością jest to najbliżej tego, co pierwotnie chciałeś (zeruj wszystkie pola oprócz tych, które chcesz zainicjować).
źródło
static address temp_address = {};
będzie działać. Tak, wypełnienie go później zależy od czasu wykonywania. Można ominąć ten poprzez dostarczanie funkcję statycznego, który nie init dla Ciebie:static address temp_address = init_my_temp_address();
.init_my_temp_address
może być funkcją lambda:static address temp_address = [] () { /* initialization code */ }();
address
a nigdy nie poznasz wszystkich miejsc, które go tworzą,address
a teraz nie inicjujesz nowego członka.Jak wspomnieli inni, jest to inicjator.
Ta funkcja jest częścią C ++ 20
źródło
Identyfikatory pól są rzeczywiście składnią inicjalizacyjną C. W C ++ podaj wartości we właściwej kolejności bez nazw pól. Niestety oznacza to, że musisz podać je wszystkie (w rzeczywistości możesz pominąć końcowe pola o zerowej wartości, a wynik będzie taki sam):
źródło
Ta funkcja nazywa się wyznaczonymi inicjalizatorami . Jest dodatkiem do standardu C99. Jednak ta funkcja została pominięta w C ++ 11. Zgodnie z językiem programowania C ++, wydanie czwarte, sekcja 44.3.3.2 (Funkcje C niezaadaptowane przez C ++):
Gramatyka C99 ma wyznaczone inicjatory [Patrz ISO / IEC 9899: 2011, Projekt komitetu N1570 - 12 kwietnia 2011 r.]
6.7.9 Inicjalizacja
Z drugiej strony C ++ 11 nie ma wyznaczonych inicjatorów [Patrz ISO / IEC 14882: 2011, N3690 Komitet Projekt - 15 maja 2013 r.]
8.5 Inicjatory
Aby osiągnąć ten sam efekt, użyj konstruktorów lub list inicjalizacyjnych:
źródło
Możesz po prostu zainicjować za pomocą ctor:
źródło
struct address
. Ponadto typy POD często celowo nie mają konstruktora i destruktora.Możesz nawet spakować rozwiązanie Gui13 do pojedynczej instrukcji inicjalizacji:
Oświadczenie: Nie polecam tego stylu
źródło
address
a kod nadal będzie się kompilował z milionem miejsc inicjalizujących tylko pierwszych pięciu członków. Najlepszą częścią inicjalizacji struktury jest to, że możesz mieć wszystkich członków,const
a to zmusi cię do zainicjowania ich wszystkichWiem, że to pytanie jest dość stare, ale znalazłem inny sposób inicjowania, używając constexpr i curry:
Ta metoda działa również w przypadku globalnych zmiennych statycznych, a nawet zmiennych constexpr. Jedyną wadą jest zła konserwowalność: za każdym razem, gdy trzeba zainicjować innego członka za pomocą tej metody, wszystkie metody inicjowania członka muszą zostać zmienione.
źródło
Być może coś mi tu brakuje, dlaczego nie:
źródło
Nie jest zaimplementowany w C ++. (także
char*
ciągi? Mam nadzieję, że nie).Zwykle, jeśli masz tak wiele parametrów, jest to dość poważny zapach kodu. Ale zamiast tego, dlaczego nie zainicjować wartości struktury, a następnie przypisać każdego członka?
źródło
char*
łańcuchy? Mam nadzieję, że nie).” - Cóż, to jest przykład C.char*
aleconst char*
jest o wiele bardziej bezpieczny dla typu i wszyscy po prostu używają,std::string
ponieważ jest o wiele bardziej niezawodny.Znalazłem ten sposób na zrobienie tego dla zmiennych globalnych, które nie wymagają modyfikacji oryginalnej definicji struktury:
następnie zadeklaruj zmienną nowego typu odziedziczoną z oryginalnego typu struktury i użyj konstruktora do inicjalizacji pól:
Jednak nie tak elegancki jak styl C ...
W przypadku zmiennej lokalnej wymaga ona dodatkowej pamięci (this, 0, sizeof (* this)) na początku konstruktora, więc wyraźnie nie jest gorzej, a odpowiedź @ gui13 jest bardziej odpowiednia.
(Zauważ, że „temp_address” jest zmienną typu „temp_address”, jednak ten nowy typ dziedziczy po „adres” i może być używany w każdym miejscu, w którym oczekiwany jest „adres”, więc jest OK.)
źródło
Napotkałem dzisiaj podobny problem, w którym mam strukturę, którą chcę wypełnić danymi testowymi, które zostaną przekazane jako argumenty funkcji, którą testuję. Chciałem mieć wektor tych struktur i szukałem metody jednowierszowej do inicjalizacji każdej struktury.
Skończyłem z funkcją konstruktora w strukturze, która, jak sądzę, została również zasugerowana w kilku odpowiedziach na twoje pytanie.
Prawdopodobnie złą praktyką jest, aby argumenty konstruktora miały takie same nazwy jak publiczne zmienne składowe, co wymaga użycia
this
wskaźnika. Ktoś może zaproponować edycję, jeśli istnieje lepszy sposób.Którego użyłem w mojej funkcji testowej do wywołania testowanej funkcji z różnymi argumentami takimi jak ten:
źródło
Jest to możliwe, ale tylko wtedy, gdy inicjowana struktura jest strukturą POD (zwykłe stare dane). Nie może zawierać żadnych metod, konstruktorów ani nawet wartości domyślnych.
źródło
W C ++ inicjatory w stylu C zostały zastąpione konstruktorami, które przez czas kompilacji mogą zapewnić, że wykonywane są tylko prawidłowe inicjalizacje (tj. Po inicjalizacji elementy obiektu są spójne).
Jest to dobra praktyka, ale czasami przydatna jest wstępna inicjalizacja, jak w twoim przykładzie. OOP rozwiązuje to za pomocą klas abstrakcyjnych lub wzorców projektowych .
Moim zdaniem użycie tego bezpiecznego sposobu zabija prostotę, a czasem kompromis w zakresie bezpieczeństwa może być zbyt kosztowny, ponieważ prosty kod nie wymaga skomplikowanego projektu, aby był łatwy w utrzymaniu.
Jako alternatywne rozwiązanie sugeruję zdefiniowanie makr za pomocą lambd, aby uprościć inicjalizację, aby wyglądała prawie jak w stylu C:
Makro ADRES rozwija się do
który tworzy i nazywa lambda. Makroparametry są również oddzielone przecinkami, więc musisz umieścić inicjator w nawiasach i wywołać jak
Możesz także napisać ogólny inicjalizator makr
ale wtedy połączenie jest nieco mniej piękne
można jednak łatwo zdefiniować makro ADRES przy użyciu ogólnego makra INIT
źródło
W GNUC ++ (wydaje się być przestarzały od 2.5, dawno temu :) Zobacz odpowiedzi tutaj: Inicjalizacja struktury C za pomocą etykiet. Działa, ale jak? ), możliwe jest zainicjowanie struktury takiej jak ta:
źródło
Zainspirowany tą naprawdę zgrabną odpowiedzią: ( https://stackoverflow.com/a/49572324/4808079 )
Możesz zrobić zamknięcia Lamba:
.
Lub, jeśli chcesz być bardzo fantazyjny
Wiąże się to z pewnymi wadami, głównie związanymi z niezainicjowanymi członkami. Z tego, co mówią komentarze w odpowiedziach połączonych, kompiluje się wydajnie, chociaż go nie testowałem.
Ogólnie uważam, że to dobre podejście.
źródło