Ką reiškia aiškus raktinis žodis?

Ką reiškia explicit raktinis žodis C ++?

2387
23 сент. nustatė Skizz , rugsėjo 23 d 2008-09-23 16:58 '08 at 4:58 pm 2008-09-23 16:58
@ 11 atsakymų

Kompiliatoriui leidžiama atlikti vieną numanomą konversiją, kad išspręstų funkcijų parametrai. Tai reiškia, kad kompiliatorius gali naudoti konstruktorius, kurie vadinami vienu parametru, kad konvertuotų iš vieno tipo į kitą, kad gautų teisingą parametro tipą.

Toliau pateikiamas pavyzdys su konstruktoriumi, kuris gali būti naudojamas netiesioginėms konversijoms:

 class Foo { public: // single parameter constructor, can be used as an implicit conversion Foo (int foo) : m_foo (foo) { } int GetFoo () { return m_foo; } private: int m_foo; }; 

Čia yra paprasta funkcija, kuri priima „ Foo objektą:

 void DoBar (Foo foo) { int i = foo.GetFoo (); } 

ir čia, kur DoBar funkcija.

 int main () { DoBar (42); } 

Argumentas nėra „ Foo objektas, bet << 26>. Tačiau yra „ Foo konstruktorius, kuris užima int , taigi šis konstruktorius gali būti naudojamas parametrui konvertuoti į teisingą tipą.

Kompiliatoriui leidžiama tai daryti vieną kartą kiekvienam parametrams.

Konstruktoriui explicit pateikto raktinio žodžio prefiksas neleidžia kompiliatoriui naudoti šio konstruktoriaus netiesioginėms konversijoms. Įtraukus ją į aukščiau paminėtą klasę, kompiuteryje atsiranda klaida, kai skambinate „ DoBar (42) funkcija. Dabar reikia aiškiai paskambinti konversijai naudojant „ DoBar (Foo (42))

Priežastis, dėl kurios norite tai padaryti, yra išvengti atsitiktinės konstrukcijos, kuri gali paslėpti klaidas. Mąstantis pavyzdys:

  • Turite klasę MyString(int size) su konstruktoriumi, kuris sukuria tam tikro dydžio eilutę. Jūs turite print(const MyString> funkciją ir skambinate print(3) (kai iš tikrųjų ketinate skambinti print("3") ). Tikitės, kad jis išspausdins „3“, bet spausdina tuščią 3 eilutės eilutę.
2778
23 сент. atsakymas pateikiamas Skizz 23 rugsėjo. 2008-09-23 16:59 '08 at 16:59 pm 2008-09-23 16:59

Tarkime, kad turite String :

 class String { public: String(int n); // allocate n bytes to the String object String(const char *p); // initializes object with char *p }; 

Dabar, jei bandysite:

border=0
 String mystring = 'x'; 

'x' simbolis bus netiesiogiai konvertuojamas į int , o tada - „ String(int) konstruktorius. Tačiau tai ne tai, ką vartotojas gali numatyti. Taigi, siekiant užkirsti kelią tokioms sąlygoms, mes nustatysime konstruktorių kaip explicit :

 class String { public: explicit String (int n); //allocate n bytes String(const char *p); // initialize sobject with string p }; 
971
23 сент. atsakymą pateikė Eddie Sep 23 2008-09-23 17:09 '08 at 17:09 2008-09-23 17:09

C ++ sistemoje konstruktorius su vienu reikiamu parametru yra laikomas netiesiogine konversijos funkcija. Jis konvertuoja parametro tipą į klasės tipą. Ar tai yra geras dalykas, ar ne, priklauso nuo konstruktoriaus semantikos.

Pavyzdžiui, jei turite eilutės klasę su String(const char* s) konstruktoriumi String(const char* s) , galbūt taip norite. Jūs galite perduoti const char* funkciją, kuri tikisi String , ir kompiliatorius automatiškai sukurs laikiną String objektą.

Kita vertus, jei turite buferio klasę, Buffer(int size) konstruktorius atsižvelgia į buferio dydį baitais, tikriausiai nenorite, kad kompiliatorius lengvai konvertuotų int į Buffer . Jei norite to išvengti, jūs paskelbiate konstruktorių su raktiniu žodžiu:

 class Buffer { explicit Buffer(int size); ... } 

Taigi,

 void useBuffer(Buffer buf); useBuffer(4); 

tampa kompiliavimo laiko klaida. Jei norite perduoti laikiną Buffer objektą, turite tai padaryti aiškiai:

 useBuffer(Buffer(4)); 

Taigi, jei jūsų vieno parametro konstruktorius parametrą paverčia jūsų klasės objektu, tikriausiai nenorite naudoti raktinio žodžio. Bet jei turite konstruktorių, kuris tik užima vieną parametrą, turite jį paskelbti kaip explicit , kad kompiliatorius nenustebintų netikėtų konversijų.

133
23 сент. atsakymas pateikiamas cjm 23 sep. 2008-09-23 19:37 '08 at 7:37 pm 2008-09-23 19:37

Šis atsakymas susijęs su objekto sukūrimu su / be aiškaus konstruktoriaus, nes jis nėra įtrauktas į kitus atsakymus.

Apsvarstykite šią klasę be aiškaus konstruktoriaus:

 class Foo { public: Foo(int x) : m_x(x) { } private: int m_x; }; 

Foo klasės objektai gali būti sukurti dviem būdais:

 Foo bar1(10); Foo bar2 = 20; 

Priklausomai nuo įgyvendinimo, antrasis būdas sukurti „Foo“ klasės egzempliorių gali būti painus, ar ne tai, ką planuoja programuotojas. explicit raktinio žodžio prefiksas konstruktoriui generuoja kompiliatoriaus klaidą Foo bar2 = 20; ,

Paprastai gera praktika skelbti konstruktorius vienu argumentu kaip explicit , nebent tai būtų draudžiama.

Taip pat atkreipkite dėmesį, kad konstruktoriai su

  • numatytieji visų parametrų argumentai arba. \ t
  • numatytieji argumentai antrajam parametrams

abu gali būti naudojami kaip konstruktoriai su vienu argumentu. Todėl galite tai padaryti explicit .

Pavyzdžiui, jei nenorite, kad jūsų konstruktorius su vienu argumentu būtų aiškus, tai yra, jei sukuriate funktorių (žiūrėkite šiame atsakyme deklaruotą add_x struktūrą). Tokiu atveju objektas sukuriamas kaip add_x add30 = 30; tikriausiai yra prasminga.

Čia yra geras aiškių konstruktorių įrašas.

34
08 окт. Atsakyti Gautam Oct 08. 2013-10-08 17:43 '13, 17:43, 2013-10-08 17:43

Atsiradus raktiniam žodžiui konversijos konstruktorius tampa nekonvertuojančiu konstruktoriumi. Dėl to kodas yra mažiau klaidingas.

32
21 нояб. atsakymą pateikė SankararaoMajji lapkričio 21 d. 2012-11-21 05:36 '12 at 5:36 am 2012-11-21 05:36

Taip pat pateikiamas explicit raktinis žodis

  • X klasės konstruktorius, kuris negali būti naudojamas netiesiogiai konvertuoti pirmąjį (tik vieną) parametrą į X tipą

C ++ [class.conv.ctor]

1) Konstruktorius, deklaruotas be aiškios funkcijos specifikatoriaus, nurodo konversiją iš savo parametrų tipų į jo klasės tipą. Toks konstruktorius vadinamas transformatoriaus konstruktoriumi.

2) Aiškus konstruktorius konstruoja objektus tokiu pat būdu, kaip ir netiesioginiai konstruktoriai, bet tai daro tik tais atvejais, kai tiesioginė inicijavimo sintaksė (8.5) yra aiškiai naudojama arba kai naudojami metimai (5.2.9, 5.4). Numatytasis konstruktorius gali būti aiškus konstruktorius; toks konstruktorius bus naudojamas inicijuoti pagal nutylėjimą arba inicijuoti vertę (8.5).

  • arba konversijos funkcija, kuri naudojama tik tiesioginiam inicijavimui ir aiškiam konvertavimui.

C ++ [class.conv.fct]

2) Transformavimo funkcija gali būti aiški (7.1.2), tokiu atveju ji laikoma tik pasirinktiniu transformavimu tiesioginiam inicijavimui (8.5). Priešingu atveju vartotojo apibrėžti transformacijos neapsiriboja priskyrimu ir inicijavimu.

Peržiūra

Aiškus transformavimas (tiesioginis inicijavimas arba aiškus liejimo operavimas) gali būti naudojamas tik aiškioms transformavimo funkcijoms ir konstruktoriams, o netiesioginiai konstruktoriai ir transformavimo funkcijos gali būti naudojamos tiek aiškiems, tiek aiškiems transformacijoms.

  

X, Y, Z ir funkcijų foo, bar, baz naudojimo pavyzdys:

Pažvelkite į mažą struktūrų ir funkcijų konfigūraciją, kad pamatytumėte skirtumą tarp explicit ir explicit .

 struct Z { }; struct X { explicit X(int a); // X can be constructed from int explicitly explicit operator Z (); // X can be converted to Z explicitly }; struct Y{ Y(int a); // int can be implicitly converted to Y operator Z (); // Y can be implicitly converted to Z }; void foo(X x) { } void bar(Y y) { } void baz(Z z) { } 

Dizainerio pavyzdžiai:

Funkcijos argumentų konvertavimas:

 foo(2); // error: no implicit conversion int to X possible foo(X(2)); // OK: direct initialization: explicit conversion foo(static_cast<X>(2)); // OK: explicit conversion bar(2); // OK: implicit conversion via Y(int) bar(Y(2)); // OK: direct initialization bar(static_cast<Y>(2)); // OK: explicit conversion 

Objekto inicijavimas:

 X x2 = 2; // error: no implicit conversion int to X possible X x3(2); // OK: direct initialization X x4 = X(2); // OK: direct initialization X x5 = static_cast<X>(2); // OK: explicit conversion Y y2 = 2; // OK: implicit conversion via Y(int) Y y3(2); // OK: direct initialization Y y4 = Y(2); // OK: direct initialization Y y5 = static_cast<Y>(2); // OK: explicit conversion 

Konversijos funkcijų pavyzdžiai:

 X x1{ 0 }; Y y1{ 0 }; 

Funkcijos argumentų konvertavimas:

 baz(x1); // error: X not implicitly convertible to Z baz(Z(x1)); // OK: explicit initialization baz(static_cast<Z>(x1)); // OK: explicit conversion baz(y1); // OK: implicit conversion via Y::operator Z() baz(Z(y1)); // OK: direct initialization baz(static_cast<Z>(y1)); // OK: explicit conversion 

Objekto inicijavimas:

 Z z1 = x1; // error: X not implicitly convertible to Z Z z2(x1); // OK: explicit initialization Z z3 = Z(x1); // OK: explicit initialization Z z4 = static_cast<Z>(x1); // OK: explicit conversion Z z1 = y1; // OK: implicit conversion via Y::operator Z() Z z2(y1); // OK: direct initialization Z z3 = Z(y1); // OK: direct initialization Z z4 = static_cast<Z>(y1); // OK: explicit conversion 

Kodėl naudoti konversijos funkcijas ar explicit konstruktorius?

Konversijos konstruktoriai ir netiesioginės konversijos funkcijos gali sukelti neaiškumų.

Apsvarstykite struktūrą V konvertuotą į int , struktūrą U netiesiogiai konstruktyvią V ir funkciją f perkrautą atitinkamai U ir bool .

 struct V { operator bool() const { return true; } }; struct U { U(V) { } }; void f(U) { } void f(bool) { } 

Skambutis f dviprasmiškas, kai perduodamas V tipo objektas V

 V x; f(x); // error: call of overloaded 'f(V is ambiguous 

Kompiliatorius nežino, ar naudoti U konstruktorių ar konversijos funkciją, kad V objektas būtų konvertuojamas į tipą, kad pereitumėte į f .

Jei konstruktorius U arba konversijos V funkcija yra explicit , tuomet nebus dviprasmiškumo, nes bus atsižvelgta tik į netiesioginę konversiją. Jei abu yra aiškūs, skambinimas f naudojant V tipo objektą turi būti atliekamas naudojant aiškią konversiją arba liejimo operaciją.

Konversijos konstruktoriai ir netiesioginės konversijos funkcijos gali sukelti netikėtą elgesį.

Apsvarstykite vektoriaus spausdinimo funkciją:

 void print_intvector(std::vector<int> const  { for (int x : v) std::cout << x << '\n'; } 

Jei vektoriaus konstruktoriaus dydis nebūtų aiškus, tokia funkcija gali būti vadinama:

 print_intvector(3); 

Ką galima tikėtis iš tokio skambučio? Ar vienoje eilutėje yra 3 arba trys eilutės, kuriose yra 0 ? (Kur vyksta antras, kas atsitinka.)

Naudojant aiškų raktinį žodį klasės sąsajoje, naudotojo sąsaja verčia aiškiai nurodyti norimą konversiją.

Kaip rašo Bjarne Straustrup (C ++ Programming >std::duration negali būti netiesiogiai sukurta iš pirminio numerio:

Jei žinote, ką reiškia, būkite atviri.

31
11 июля '15 в 2:48 2015-07-11 02:48 Atsakymą pateikė Pixelchemist liepos 15 d. 15 val. 2:48 2015-07-11 02:48

explicit raktinis žodis gali būti naudojamas priversti konstruktorių skambinti aiškiai.

 class C{ public: explicit C(void) = default; }; int main(void){ C c(); return 0; } 

Aiškus raktinis žodis prieš „ C(void) konstruktorių C(void) sako kompiliatoriui, kad leidžiama tik aiškiai paskambinti šiam konstruktoriui.

explicit raktinių žodžių raktas taip pat gali būti naudojamas pasirinktinio tipo konvertavimo operacijose:

 class C{ public: explicit inline operator bool(void) const{ return true; } }; int main(void){ C c; bool b = static_cast<bool>(c); return 0; } 

Čia explicit raktinis žodis taiko tik aiškius veiksmus, todėl bool b = c; šiuo atveju bus neteisingas. Tokiose situacijose explicit raktinis žodis gali padėti programuotojui išvengti netiesioginių, nenumatytų viršįtampių. Šis naudojimas yra standartizuotas C ++ 11 .

25
14 мая '13 в 12:28 2013-05-14 12:28 atsakymą pateikė Helixirr , gegužės 14 d. 13 val. 12:28 2013-05-14 12:28

Tai jau buvo aptarta ( kas yra aiškus konstruktorius ). Bet turiu pasakyti, kad jam trūksta išsamių aprašymų.

Be to, visada yra gera kodavimo praktika sukurti savo argumentų konstruktorius (įskaitant tuos, kurie turi numatytas reikšmes arg2, arg3, ...), kaip jau buvo pasakyta. Kaip visada su C + +: jei to nepadarysite, jūs norėsite, kad ...

Kitas geras užsiėmimų klasių pavyzdys - kopijavimas ir priskyrimas privačiam (tai yra išjungti jį išjungti), jei jums to nereikia. Taip išvengsite galimų nukreipimų kopijų, kai naudojate metodus, kuriuos C ++ sukuria jums pagal nutylėjimą. Kitas būdas tai padaryti yra gauti padidinimą: noncopyable.

17
02 окт. atsakymas pateikiamas fmuecke 02 oct. 2009-10-02 01:00 '09, 1:00 val. 2009-10-02 01:00

Nuoroda Cpp visada naudinga! Išsamią informaciją apie aiškų specifiką rasite čia . Gali tekti pažvelgti į netiesiogines konversijas ir kopijavimo inicijavimą .

Greita išvaizda

Aiškus nurodymas rodo, kad konstruktoriaus arba konversijos funkcija (nuo C ++ 11) neleidžia kopijuoti ar inicijuoti netiesiogiai.

Pavyzdys yra toks:

 struct A { A(int) { } // converting constructor A(int, int) { } // converting constructor (C++11) operator bool() const { return true; } }; struct B { explicit B(int) { } explicit B(int, int) { } explicit operator bool() const { return true; } }; int main() { A a1 = 1; // OK: copy-initialization selects A::A(int) A a2(2); // OK: direct-initialization selects A::A(int) A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int) A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int) A a5 = (A)1; // OK: explicit cast performs static_cast if (a1) cout << "true" << endl; // OK: A::operator bool() bool na1 = a1; // OK: copy-initialization selects A::operator bool() bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization // B b1 = 1; // error: copy-initialization does not consider B::B(int) B b2(2); // OK: direct-initialization selects B::B(int) B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int) // B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int) B b5 = (B)1; // OK: explicit cast performs static_cast if (b5) cout << "true" << endl; // OK: B::operator bool() // bool nb1 = b2; // error: copy-initialization does not consider B::operator bool() bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization } 
15
20 авг. atsakymas duodamas 20 sav . 2016-08-20 15:45 '16 at 15:45 2016-08-20 15:45

Aiškūs konversijos konstruktoriai (tik „C ++“)

Aiškus funkcijų specifikatorius valdo nepageidaujamą netiesioginį konversijos tipą. Jis gali būti naudojamas tik konstruktoriaus deklaracijose, pateiktose klasės deklaracijoje. Pavyzdžiui, ne tik konstruktorius, bet ir kitos klasės konstruktoriai yra Konstruktoriai.

 class A { public: A(); A(int); A(const char*, int = 0); }; 

Šie skelbimai yra teisėti:

 A c = 1; A d = "Venditti"; 

Pirmoji deklaracija yra lygiavertė A c = A( 1 ); .

Jei skelbiate klasės konstruktorių kaip explicit , ankstesnės deklaracijos būtų neteisėtos.

Pavyzdžiui, jei paskelbiate klasę kaip:

 class A { public: explicit A(); explicit A(int); explicit A(const char*, int = 0); }; 

Galite priskirti tik klases, atitinkančias klasės tipo vertes.

Pavyzdžiui, šie teiginiai yra teisėti:

  A a1; A a2 = A(1); A a3(1); A a4 = A("Venditti"); A* p = new A(1); A a5 = (A)1; A a6 = static_cast<A>(1); 
10
20 дек. Atsakymas pateikiamas coding_ninza 20 d. 2017-12-20 15:19 '17 15:19 2017-12-20 15:19