Gieneq Napisano Luty 18, 2023 Udostępnij Napisano Luty 18, 2023 Pisząc pytanie udało mi się na nie odpowiedzieć 😅 więc zostawię to jako ewentualny temat do dyskusji. W C++ jest mały bałagan (albo za dużo możliwości) i podobno w CoreGuidelines jest gdzieś napisanne czego używać i gdzie. Przykładowo to trochę rzuca światła na sytuację: vector<int> v1(10); // vector of 10 elements with the default value 0 vector<int> v2{10}; // vector of 1 element with the value 10 vector<int> v3(1, 2); // vector of 1 element with the value 2 vector<int> v4{1, 2}; // vector of 2 elements with the values 1 and 2 Napisane jest też, że używanie {} do zakładania zmiennych pozwala uniknąć niejawnego (implicite) rzutowania np z double na int, więc taki zapis jest preferowany: double d{1.2}; int i{d}; //warning: narrowing conversion of 'd' from 'double' to 'int' inside { } Dalej: int v; // losowa wartość (Clang) lub 0 (GCC) std::String s; // konstruktor domyślny i wynik "" Zgodnie z regułą zera zakładajac własny obiekt dobrze jest korzystać z domyślnych konstruktorów/destruktorów obiektów. Zakładam klasę: struct MyPoint { int x; int y; friend std::ostream& operator<<(std::ostream &os, const MyPoint& p); }; std::ostream& operator<<(std::ostream &os, const MyPoint& p) { os << "(" << p.x << ", " << p.y << ")"; return os; } I testuję: MyPoint p1; // konstruktor domyślny MyPoint p2{}; // lista inicjalizująca lub jak nie ma to konstruktor domyślny MyPoint p3(); // ????? Pytanie: co robi to ostatnie? Wartość tego to 1, podobnie jak wartość int p(), itp. Ostrzeżenie to: main.cpp: In function 'int main()': main.cpp:25:14: warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse] 25 | MyPoint p(); | ^~ main.cpp:25:14: note: remove parentheses to default-initialize a variable 25 | MyPoint p(); | ^~ | -- main.cpp:25:14: note: or replace parentheses with braces to aggregate-initialize a variable main.cpp:28:18: warning: the address of 'MyPoint p()' will never be NULL [-Waddress] 28 | std::cout << p << std::endl; | ^ main.cpp:25:13: note: 'MyPoint p()' declared here 25 | MyPoint p(); | ^ Czyli w tym miejscu jest jakby deklaracja funkcji, tak samo jakbym napisał: MyPoint p(); int main() { MyPoint p1 = p(); } Czyli ten zapis nie pasuje do konstruktora domyślnego (bez argumentów), bo wygląda jak zwykła funkcja - tak przynajmiej twierdzi kompilator. Ciekawe że gdy w klasie istnieje tylko konstruktor z niezerową liczbą argumentów, to domyslny konstruktor znika: MyPoint(int v) : x{v}, y{v} {} // tu na liście inicjalizującej mogą być (), ale nie ma ograniczeń ... int main() { MyPoint p4(33); // (33, 33) MyPoint p5; // błąd - nie ma domyślnego } Więc trzeba go dodać. Wersja domyślna potraktuje domyślnie zmienne składowe (reguła zera). Tylko patrząc na to jak traktowane są typy prymityne lepiej nie zakładać że będą mieć wartość 0 i lepiej ręcznie te 0 wpisać. MyPoint(int v) : x{v}, y{v} {} MyPoint() = default; // << // lub: MyPoint() : x{}, y{} {} // lub: MyPoint() : x{0}, y{0} {} // lub: MyPoint() : x(0), y(0) {} // lub: MyPoint() : x(), y() {} //OK // lub: MyPoint() {x = 0; y = 0;} ... int main() { MyPoint p4(33); // (33, 33) MyPoint p5; // OK } Będąc w temacie można jeszcze dodać, że użycie słowa kluczowego explicite może uchronić przed przypadkową konwersją: explicit MyPoint(int v) : x{v}, y{v} {} MyPoint() = default; ... int main() { MyPoint p4 = 33; // Error, explicit! MyPoint p5{33}; // OK } Wracając do sytuacji z pierwszego przykładu, można używać () {} do wyboru rodzaju konstruktora: #include <iostream> #include <string> #include <vector> #include <initializer_list> struct MyPoint { std::vector<int> v; MyPoint() = default; MyPoint(int p) : v{p} {}; MyPoint(int p1, int p2) : v{p1, p2} {}; MyPoint(std::initializer_list<int> && list) : v{list} {}; friend std::ostream& operator<<(std::ostream &os, const MyPoint& p); }; std::ostream& operator<<(std::ostream &os, const MyPoint& p) { for (auto& t : p.v) os << t << ", "; return os; } int main() { MyPoint p1{122}; // konstruktor lista inicjalizująca 1 element MyPoint p2(1, 2); // konstruktor 2 arrgumenty MyPoint p3{1, 2}; // konstruktor lista inicjalizująca 2 elementy MyPoint p4({1, 2}); // konstruktor lista inicjalizująca 2 elementy, jak wyżej MyPoint p5{1, 2, 3}; // konstruktor lista inicjalizująca 3 elementy //MyPoint p6(1, 2, 3); // błąd! std::cout << p1 << std::endl; //122, std::cout << p2 << std::endl; //1, 2, std::cout << p3 << std::endl; //1, 2, std::cout << p4 << std::endl; //1, 2, std::cout << p5 << std::endl; //1, 2, 3, return 0; } Żeby trochę zamieszać możemy przeciążyć operator (), wtedy sytuacja może wyglądać tak: int operator()() { return -123; } ... int main() { MyPoint p4; auto pv = p4(); //-123 int } i na zakończenie napisać takiego stwora: auto v = MyPoint{}(); //lub MyPoint()() Wnioski: Inicjowanie zmiennych {} jest preferowane bo: unika się niejawnej konwersji typów, automatycznie dobiera przeciężony konstruktor wychodząc od listy inicjalizującej i spadając na konstruktora z daną liczbą argumentów, nie dojdzie do przypadkowego pomylenia z funkcją, gdy mamy przeciążony konstruktor z listą inicjalizującą można użyć (), {} do wyboru wariantu konstruktora, nazwa_zmiennej() to nie konstruktor domyślny taki jak NazwaKlasy() 1 Link do komentarza Share on other sites More sharing options...
Popularny post H1M4W4R1 Luty 18, 2023 Popularny post Udostępnij Luty 18, 2023 42 minuty temu, Gieneq napisał: W C++ jest mały bałagan (albo za dużo możliwości) Zapraszam do C# 🙂 To C z domieszką C++ wzorowane na Javie z ilością operatorów rodem z jakiegoś starego ASM... Ogólnie większość języków wysokiego poziomu ma wiele nadmiarowych rozwiązań, by każdy mógł pisać jak chce... Niestety potem to prowadzi do strasznego bałaganu w kodzie gdy kilka osób pracuje przy jednym projekcie. Ale coś za coś... Versatility reduces reliability. 3 Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Bądź aktywny - zaloguj się lub utwórz konto!
Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony
Utwórz konto w ~20 sekund!
Zarejestruj nowe konto, to proste!
Zarejestruj się »Zaloguj się
Posiadasz własne konto? Użyj go!
Zaloguj się »