new[]-array und konstruktoren



  • Hi!
    Wenn ich mit new[] ein Array angelegt habe, wird dann für jedes Element der Standard-Konstruktor aufgerufen?
    Oder muss ich dann für jedes Element einen bestimmten Konstruktor (nochmal mit new()?) aufrufen, wobei ich dann aber schon bei CMyClass** liege, oder?
    Wie regel ich das?
    danke
    Sören


  • Mod

    soerenP schrieb:

    Hi!
    Wenn ich mit new[] ein Array angelegt habe, wird dann für jedes Element der Standard-Konstruktor aufgerufen?

    Ohne Klammern (new T[...]): Defaultkonstruktor wird aufgerufen, wenn T kein POD ist.
    Mit Klammern ( new T[...]() ): Defaultkonstruktor wird immer aufgerufen.



  • camper schrieb:

    Ohne Klammern (new T[...]): Defaultkonstruktor wird aufgerufen, wenn T kein POD ist.
    Mit Klammern ( new T[...]() ): Defaultkonstruktor wird immer aufgerufen.

    Gibts überhaupt Fälle, wo das einen Unterschied macht? Schließlich werden PODs doch gar nicht initialisiert, oder etwa doch 😕


  • Mod

    Badestrand schrieb:

    camper schrieb:

    Ohne Klammern (new T[...]): Defaultkonstruktor wird aufgerufen, wenn T kein POD ist.
    Mit Klammern ( new T[...]() ): Defaultkonstruktor wird immer aufgerufen.

    Gibts überhaupt Fälle, wo das einen Unterschied macht?

    Immer dann, wenn ich initialisierte PODs haben will? Defaultinitialisierung bei PODs ist Zero-Initialisierung - bei einer POD-Struktur werden also alle Member mit 0(geeignet konvertiert) initialisiert.

    wenn ich new char[...] schreibe, erhalte ich einen Block mit unbekanntem Inhalt. bei new char[...]() steht überall 0 drin.



  • Oh, was man noch alles lernen kann 😃



  • und mit new[](bla, bla)
    ruf ich andere Konstruktoren auf?
    Das geht? 😮 Dachte, das würde er nicht wollen.
    Probier ich mal aus, vielen Dank!
    Sören


  • Mod

    soerenP schrieb:

    und mit new[](bla, bla)
    ruf ich andere Konstruktoren auf?

    leider nicht.



  • Scheint nicht zu gehen 😞
    "Angabe der Initialisierung für Felder nicht möglich."
    Führt da kein weg an einer nachfolgenden Schleife mit gnz vielen init(bla, bla) aufrufen vorbei?
    Grüße
    Sören



  • soerenP schrieb:

    Führt da kein weg an einer nachfolgenden Schleife mit gnz vielen init(bla, bla) aufrufen vorbei?

    Klar, verwende einen vernünftigen Container, der seine Elemente gleich initialisieren kann 😃

    //aufwendig und fehleranfällig:
    meineKlasse* data = new meineKlasse[100];
    for(int i=0;i<100;++i) data[i]=meineKlasse(x,y);
    
    //besser:
    std::vector<meineKlasse> data(100,meineKlasse(x,y));
    

  • Mod

    soerenP schrieb:

    Scheint nicht zu gehen 😞
    "Angabe der Initialisierung für Felder nicht möglich."
    Führt da kein weg an einer nachfolgenden Schleife mit gnz vielen init(bla, bla) aufrufen vorbei?
    Grüße
    Sören

    Auf (großen) Umwegen geht vieles, allerdings ist das generisch kaum in den Griff zu kriegen:

    struct T;
    extern T proto;
    struct T
    {
        T(const T& = proto) : /* ... */  {}  // Copy- und Default-ctor
        explicit T(... ) /* ... */
    // ...
    };
    
    T proto( ... );  // Defaultkonstruktor vermeiden.
    
    // ...
    
    proto=...
    new T[...]()  // Alle Elemente werden als Kopie von proto initialisiert
    

    so oder ähnlich geht das schon, ist aber ziemlich umständlich und hässlich

    Der normale Weg ist die manuelle Allokation und dann placement new in einer Schleife. Ob man darauf dann delete[] anwenden darf, ist dabei allerdings etwas problematisch - zumindest in generischem Code sollte man das unterlassen.



  • Oha, tief in die Trickkiste gegriffen. Vielen Dank. Ich bekomms auch so hin;)
    Tschö
    Sören


  • Mod

    ich hab mal ne kleine Templatefunktion geschrieben, die ein new[] mit beliebigem Konstruktoraufruf durchführt (löschen wie gehabt mit delete[]):

    #include <cstddef>
    #include <memory>
    #include <ostream>
    #include <iostream>
    
    template <typename T>
    T* call_operator_new_array(std::size_t n, ...)
    {
        return static_cast< T* >( ::operator new[]( n ) );
    }
    
    template <typename T>
    T* call_operator_new_array(std::size_t n, int(*)[sizeof(T::operator new[]( n ))])
    {
        return static_cast< T* >( T::operator new[]( n ) );
    }
    
    template <typename T>
    void call_operator_delete_array(T* p, ...)
    {
        ::operator delete[]( p );
    }
    
    template <typename T>
    void call_operator_delete_array(T* p, int(*)[sizeof(T::operator delete[]( p ),0)])
    {
        T::operator delete[]( p );
    }
    
    template <typename T>
    T* new_array(std::size_t n)
    {
        T* p = call_operator_new_array< T >( n, 0 );
        std::size_t i = 0;
        try
        {
            for ( ; i < n; ++i )
                ::new ( p + i ) T();
            return p;
        }
        catch (...)
        {
            for ( ; i; )
                p[ --i ].~T();
            call_operator_delete_array( p, 0 );
            throw;
        }
    }
    
    template <typename T, typename U1>
    T* new_array(std::size_t n, U1 arg1)
    {
        T* p = call_operator_new_array< T >( n, 0 );
        std::size_t i = 0;
        try
        {
            for ( ; i < n; ++i )
                ::new ( p + i ) T( arg1 );
            return p;
        }
        catch (...)
        {
            for ( ; i; )
                p[ --i ].~T();
            call_operator_delete_array( p, 0 );
            throw;
        }
    }
    
    template <typename T, typename U1, typename U2>
    T* new_array(std::size_t n, U1 arg1, U2 arg2)
    {
        T* p = call_operator_new_array< T >( n, 0 );
        std::size_t i = 0;
        try
        {
            for ( ; i < n; ++i )
                ::new ( p + i ) T( arg1, arg2 );
            return p;
        }
        catch (...)
        {
            for ( ; i; )
                p[ --i ].~T();
            call_operator_delete_array( p, 0 );
            throw;
        }
    }
    // usw.
    
    struct T {};
    struct U
    {
        static void* operator new[](std::size_t n) { std::cout << "U::operator new[]" << std::endl; return ::operator new[]( n ); }
        static void operator delete[](void* p) { std::cout << "U::operator delete[]" << std::endl; ::operator delete[]( p ); }
    };
    
    int main()
    {
        new_array<int>(10);
        new_array<T>(10);
        new_array<U>(10);
    }
    

    dummerweise klappt es nicht mit vc; g++ ist ungetestet, comeau hat keine Probleme. vielleicht hat ja jemand eine Idee, wie man das Ganze mit vc zum laufen kriegt. Allgemein besteht die Problematik darin, abhängig davon, ob ein Ausdruck wohlgeformt ist oder nicht, eine entsprechende Überladung auszuwählen.


Anmelden zum Antworten