Frage - Klasseninitialisierung



  • asc schrieb:

    ...Explizit bei einem Konstruktor heißt, das dieser keine impliziten Umwandlung vornehmen darf. Sprich wenn du einer Klasse nur einem expliziten Konstruktor mit einem int-Wert als Parameter gibst, kannst du diese nicht mit einem short-Wert konstruieren.

    Explizite Konstruktoren sind in der Regel den impliziten vorzusehen wenn es um Typsicherheit geht......

    Warum geht das hier dann?

    #include <iostream>
    using namespace std;
    
    class Foo
    {
        public:
        explicit Foo(int a1_);
        ~Foo();
    
        private:
        int m_a1;
    
    };
    
    Foo::Foo(int a1_)
    :m_a1(a1_)
    {
    
    }
    
    Foo::~Foo()
    {
    
    }
    
    int main()
    {
        short x=13;
        Foo test=Foo(x);
    
        return 0;
    }
    

    MfG
    Stromberg



  • Weil asc dort etwas falsches geschrieben hat - 'explicit' verhindert nicht die internen Typumwandlungen von C++, es verhindert nur die implizite Umwandlung von int nach foo (in deinem Beispiel), d.h. die Verwendung foo x=10; oder die Verwendung eines int-Wertes für einen foo.

    @asc: Und es gibt zwar mitunter mehrere implizite Umwandlungen hintereinander, aber davon maximal eine benutzerdefinierte (Konstruktor oder Umwandlungsoperator).



  • Die explizite Typumwandlung verhindert (so wie CStoll schon schrieb) die automatische Typumwandlung bei benutzerdefineirten Typen.
    Sie kommt erst dann wirklich zur Geltung, wenn mehrere Klassen im Spiel sind, z.B.

    class Foo
    {
        explicit Foo(int a);
    };
    
    class Bar
    {
    public:
        Bar(const Foo &foo);
    };
    
    int main()
    {
       Bar b = 42; // dies führt jetzt zu einem Fehler!
    }
    

    Ohne die Angabe von 'explicit' bei Foo würde die obere Anweisung vom Compiler als

    Bar b = Bar(Foo(42)); // bzw. dies ist gleichwertig mit: Bar b(Foo(42))
    

    interpretiert.

    P.S: Stroustrup gibt in seinem Buch auch noch als Beispiel:

    // selbsterstellte Klasse
    class String
    {
      explicit String(int n); // Vorbelegung von n Zeichen
    };
    
    String s(10); // Initialisierung mit 10 Zeichen
    String s('a'); // <- Fehler, ohne explicit würden 65 Zeichen vorbelegt
    

    Eine sogenannte Promotion ist dagegen immer möglich, also von short<->int<->long.



  • Interessanterweise sollte das der Compiler auch ohne explicit abweisen - es ist maximal eine nutzerdefinierte implizite Umwandlung am Stück erlaubt. (Edit: Und so einen Ansatz habe ich schon als Workaround gesehen für Compiler, die nichts mit explicit anfangen können)

    Hier mal ein Beispiel, wo es wirklich einen Unterschied macht:

    class test
    {
    public:
      explicit test(int);
    private:
      ...
    };
    
    void print(const test& value);
    
    ...
    print(4711);      //verboten - Umwandlung ist explizit
    print(test(4711));//erlaubt
    ...
    //und hier der kleine, aber feine Unterschied:
    test i = 100;     //verboten
    test i(100);      //erlaubt
    


  • Das hier funktioniert bei mir nicht, mit "explicit" oder ohne "explicit":

    #include <iostream>
    using namespace std;
    
    class Foo
    {
        public:
        explicit Foo(int a);
        private:
        int m_x;
    };
    
    Foo::Foo(int a)
    :m_x(a)
    {
    
    }
    
    class Bar
    {
        public:
        Bar(const Foo &foo);
        private:
        Foo m_y;
    };
    
    Bar::Bar(const Foo &foo)
    :m_y(foo)
    {
    
    }
    
    int main()
    {
       Bar b = 42; // dies führt jetzt zu einem Fehler!
    }
    

    Außerdem wäre für den Compiler "Bar b = 42" u. "Bar b = Foo(42)" immer --> Bar b = Bar(Foo(42));???

    Und was ist jetzt eigentlich nochmal die Poente von "explicit"? Was soll damit verhindert bzw. bezweckt werden? Wo liegt der Sinn?

    MfG
    Stromberg



  • Stromberg schrieb:

    Und was ist jetzt eigentlich nochmal die Poente von "explicit"? Was soll damit verhindert bzw. bezweckt werden? Wo liegt der Sinn?

    class a_explicit
    {
    public:
        a_explicit() {}
        explicit a_explicit(int a){}
    };
    
    class a_implicit
    {
    public:
        a_implicit() {}
        a_implicit(int a){}
    };
    
    void foo_e(a_explicit const& ae)
    {
    }
    
    void foo_i(a_implicit const& ai)
    {
    }
    
    void bar()
    {
        foo_e(1); // geht nicht
        foo_i(1); // geht schon
    
        a_explicit ae;
        a_implicit ai;
        ae = 1; // geht nicht
        ai = 1; // geht schon
    }
    

    Der Sinn ist also implizite Konvertierungen zu "verbieten" wo sie sonst erlaubt wären. Und das ist meistens das was man möchte. -> einfach immer "explicit" vor jeden Ctor schreiben, ausser man möchte ganz bewusst implizite Konvertierungen erlauben (wie z.B. std::string::string(const char*) damit man String-Literale verwenden kann wo ein std::string erwartet wird).



  • Muss ich eigentlich eine implizite Initialisierung als "copy initialization" ansehen, oder kann ich das auch als "direct initialization" ansehen, weil eigentlich ist es doch so, das "(...)" == "=..." ist oder?
    int a=10;
    int a(10);
    ???
    Also:

    class A; //Konstruktor implizit
    
    int main()
    {
        A=12; //Passiert da dann dass hier --> A=A(12) --> Kopierkonstruktor
              //oder dass hier --> A(12) --> normaler Konstruktor
        return 0;
    }
    

    Oder kann man das nicht so genau sagen, da es immer irgendwie als Konstruktor weg optimiert wird? Oder wie läuft das nochmal ab?

    MfG
    Stromberg



  • Soweit ich weiß benutzt der Compiler da immer den normalen Konstruktor, außer auf der rechten Seite ist auch ein Objekt vom Typ A, dann natürlich den Copy-Konstruktor



  • Initialisierung mit "A a = 12;" ist copy initialization, wobei aber der copy-ctor "elided" (weggelassen) werden darf.
    Ist also AFAIK äquivalent zu "A a = A(12);" bzw. "A a(A(12));".



  • Wie man sich täuschen kann 🤡


Anmelden zum Antworten