Frage - Klasseninitialisierung



  • Stromberg schrieb:

    1. Gibt es für "implizit" noch eine Andere Form? Weil wie schön des öfteren gesagt, verstehe ich das sonst nicht, weil sonst kann man mit einer "implizit" copy initialization ja nur einen Parameter abdecken. Daraus folgt ja dann wohl, die Klasse die "implizit" copy initializiert wird, darf nur ein Argument im Parameter haben, bzw. müssen sonst die anderen alles einen default Parameter haben. Ist das korrekt? Oder gibts noch eine andere "implizit Form"?

    Impliziter Konstruktoraufruf ist immer dann möglich, wenn es nur ein Parameter (+beliebig viele weitere mit Defaultargumenten) gibt. Eine Implizite Konvertierung kann aber mit beliebig vielen Parametern stattfinden (z.B. wenn der dritte Parameter ein int verlangt, du aber ein short übergibst).

    Stromberg schrieb:

    2. Das ist ja eigentlich das was "asc" angesprochen hat. Mit dem "short" an ein "int" übergeben. Findet hier also so eine art "static_cast" statt?...

    Ja, der Compiler darf glaube ich auch 2 implizite Konvertierungen in Folge durchführen um auf ein Wert zu kommen... Sprich wenn du eine Klasse a hast, die im Konstruktor ein Argument der Klasse b verlangt, diese wieder einen Konstruktor für int hat, wird der Aufruf des Konstruktors der Klasse a mit einem int-Wert funktionieren... Was sehr gefährlich, und der eigentliche Grund für die explizite Konstruktordeklaration ist.

    Stromberg schrieb:

    3. Eigentlich verwendet man copy initializations, ob "implizit" oder "explizit" doch eher selten? Meistens nimmt man doch immer noch den Konstruktor oder? Zumal der doch auch viel schneller ist (direct_initialization).

    Auch beim impliziten Konstruktoraufruf wird dennoch der Konstruktor verwendet.

    cu André



  • 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