webentwicklung-frage-antwort-db.com.de

C ++: Konstruktorinitialisierer für Arrays

Ich habe einen Gehirnkrampf ... Wie initialisiere ich ein Array von Objekten richtig in C++?

nicht-Array-Beispiel:

struct Foo { Foo(int x) { /* ... */  } };

struct Bar { 
     Foo foo;

     Bar() : foo(4) {}
};

array-Beispiel:

struct Foo { Foo(int x) { /* ... */  } };

struct Baz { 
     Foo foo[3];

     // ??? I know the following syntax is wrong, but what's correct?
     Baz() : foo[0](4), foo[1](5), foo[2](6) {}
};

edit: Wilde und verrückte Workaround-Ideen werden geschätzt, aber sie helfen mir in meinem Fall nicht weiter. Ich arbeite an einem eingebetteten Prozessor, in dem std :: vector und andere STL-Konstrukte nicht verfügbar sind. Die offensichtliche Abhilfe besteht darin, einen Standardkonstruktor zu erstellen und eine explizite init() -Methode zu verwenden, die nach dem Konstruieren aufgerufen werden kann. Zeit, damit ich überhaupt keine Initialisierer verwenden muss. (Dies ist einer der Fälle, in denen ich durch Javas Schlüsselwort final und die Flexibilität der Konstruktoren verwöhnt wurde.)

62
Jason S

Es gibt keine Möglichkeit. Sie benötigen einen Standardkonstruktor für Array-Mitglieder, der aufgerufen wird. Anschließend können Sie im Konstruktor alle gewünschten Initialisierungen vornehmen.

49
AProgrammer

Nur um diese Frage für C++ 11 zu aktualisieren, ist dies jetzt sowohl möglich als auch sehr natürlich:

struct Foo { Foo(int x) { /* ... */  } };

struct Baz { 
     Foo foo[3];

     Baz() : foo{{4}, {5}, {6}} { }
};

Diese Klammern können auch für eine noch präzisere Darstellung entfernt werden:

struct Baz { 
     Foo foo[3];

     Baz() : foo{4, 5, 6} { }
};

Was sich auch leicht auf mehrdimensionale Arrays erweitern lässt:

struct Baz {
    Foo foo[3][2];

    Baz() : foo{1, 2, 3, 4, 5, 6} { }
};
33
Barry

Derzeit können Sie die Initialisierungsliste nicht für Array-Mitglieder verwenden. Du steckst auf die harte Tour fest.

class Baz {
    Foo foo[3];

    Baz() {
        foo[0] = Foo(4);
        foo[1] = Foo(5);
        foo[2] = Foo(6);
    }
};

In C++ 0x können Sie schreiben:

class Baz {
    Foo foo[3];

    Baz() : foo({4, 5, 6}) {}
};
16

Leider gibt es keine Möglichkeit, Array-Mitglieder bis C++ 0x zu initialisieren.

Sie könnten einen std :: vector verwenden und die Foo-Instanzen im Konstruktorkörper zurückschieben.

Sie könnten Foo einen Standardkonstruktor geben (möglicherweise privat und Baz zu einem Freund machen).

Sie können ein Array-Objekt verwenden, das is kopierbar ist (boost oder std :: tr1) und aus einem statischen Array initialisiert wird:

#include <boost/array.hpp>

struct Baz {

    boost::array<Foo, 3> foo;
    static boost::array<Foo, 3> initFoo;
    Baz() : foo(initFoo)
    {

    }
};

boost::array<Foo, 3> Baz::initFoo = { 4, 5, 6 };
7
UncleBens

Sie können das Schlüsselwort C++ 0xauto zusammen mit template specialization zum Beispiel für eine Funktion mit dem Namen boost::make_array() verwenden (ähnlich wie make_pair()). Für den Fall, dass N entweder 1 oder 2 Argumente ist, können wir Variante A as schreiben

namespace boost
{
/*! Construct Array from @p a. */
template <typename T>
boost::array<T,1> make_array(const T & a)
{
    return boost::array<T,2> ({{ a }});
}
/*! Construct Array from @p a, @p b. */
template <typename T>
boost::array<T,2> make_array(const T & a, const T & b)
{
    return boost::array<T,2> ({{ a, b }});
}
}

und Variante B als

namespace boost {
/*! Construct Array from @p a. */
template <typename T>
boost::array<T,1> make_array(const T & a)
{
    boost::array<T,1> x;
    x[0] = a;
    return x;
}
/*! Construct Array from @p a, @p b. */
template <typename T>
boost::array<T,2> make_array(const T & a, const T & b)
{
    boost::array<T,2> x;
    x[0] = a;
    x[1] = b;
    return x;
}
}

GCC-4.6 mit -std=gnu++0x Und -O3 Erzeugt den exakt gleichen Binärcode für

auto x = boost::make_array(1,2);

verwenden Sie sowohl [~ # ~] a [~ # ~] als auch [~ # ~] b [~ # ~] wie für

boost::array<int, 2> x = {{1,2}};

Für Benutzerdefinierte Typen (UDT) führt Variante B jedoch zu einem zusätzlichen Kopierkonstruktor, was normalerweise zu Verzögerungen führt und daher vermieden werden sollte.

Beachten Sie, dass boost::make_array Fehler auftreten, wenn Sie es mit expliziten Char-Array-Literalen wie im folgenden Fall aufrufen

auto x = boost::make_array("a","b");

Ich halte dies für eine gute Sache, da const char* Literale in ihrer Verwendung irreführend sein können.

Variadic templates, verfügbar in GCC seit 4.5, kann weiter verwendet werden. Reduzieren Sie den gesamten Kesselschildcode für die Template-Spezialisierung für jedes N in ein Einzelvorlagendefinition von boost::make_array() definiert als

/*! Construct Array from @p a, @p b. */
template <typename T, typename ... R>
boost::array<T,1+sizeof...(R)> make_array(T a, const R & ... b)
{
    return boost::array<T,1+sizeof...(R)>({{ a, b... }});
}

Das funktioniert so ziemlich wie wir es erwarten. Das erste Argument bestimmt das boost::array - Vorlagenargument T und alle anderen Argumente werden in T konvertiert. In einigen Fällen kann dies unerwünscht sein, ich bin mir jedoch nicht sicher, wie dies mithilfe variabler Vorlagen angegeben werden kann.

Vielleicht sollte boost::make_array() in die Boost-Bibliotheken gehen?

3
Nordlöw

Dies scheint zu funktionieren, aber ich bin nicht überzeugt, dass es richtig ist:

#include <iostream>

struct Foo { int x; Foo(int x): x(x) { } };

struct Baz { 
     Foo foo[3];

    static int bar[3];
     // Hmm...
     Baz() : foo(bar) {}
};

int Baz::bar[3] = {4, 5, 6};

int main() {
    Baz z;
    std::cout << z.foo[1].x << "\n";
}

Ausgabe:

$ make arrayinit -B CXXFLAGS=-pedantic && ./arrayinit
g++ -pedantic    arrayinit.cpp   -o arrayinit
5

Vorbehalt Emptor.

Edit: nein, Comeau lehnt es ab.

Eine weitere Änderung: Dies ist eine Art von Betrug. Sie verschiebt die Array-Initialisierung von Mitglied zu Mitglied an einen anderen Ort. Daher benötigt Foo immer noch einen Standardkonstruktor, aber wenn Sie nicht über std::vector Verfügen, können Sie das absolute Minimum, das Sie benötigen, selbst implementieren:

#include <iostream>

struct Foo { 
    int x; 
    Foo(int x): x(x) { }; 
    Foo(){}
};

// very stripped-down replacement for vector
struct Three { 
    Foo data[3]; 
    Three(int d0, int d1, int d2) {
        data[0] = d0;
        data[1] = d1;
        data[2] = d2;
    }
    Foo &operator[](int idx) { return data[idx]; }
    const Foo &operator[](int idx) const { return data[idx]; }
};

struct Baz { 
    Three foo;

    static Three bar;
    // construct foo using the copy ctor of Three with bar as parameter.
    Baz() : foo(bar) {}
    // or get rid of "bar" entirely and do this
    Baz(bool) : foo(4,5,6) {}
};

Three Baz::bar(4,5,6);

int main() {
    Baz z;
    std::cout << z.foo[1].x << "\n";
}

z.foo Ist eigentlich kein Array, sieht aber ungefähr so ​​aus wie ein Vektor. Das Hinzufügen von Funktionen begin() und end() zu Three ist trivial.

2
Steve Jessop

Beim Erstellen von Objekten in einem Array kann nur der Standardkonstruktor aufgerufen werden.

1
Robert

In dem speziellen Fall, in dem das Array ein Datenelement der Klasse ist, können Sie es nicht in der aktuellen Version der Sprache initialisieren. Dafür gibt es keine Syntax. Geben Sie entweder einen Standardkonstruktor für Array-Elemente an oder verwenden Sie std::vector.

Ein eigenständiges Array kann mit dem Aggregatinitialisierer initialisiert werden

Foo foo[3] = { 4, 5, 6 };

leider gibt es keine entsprechende Syntax für die Liste der Konstruktorinitialisierer.

1
AnT

Sie können es schaffen, aber es ist nicht schön:

#include <iostream>

class A {
    int mvalue;
public:
    A(int value) : mvalue(value) {}
    int value() { return mvalue; }
};

class B {
    // TODO: hack that respects alignment of A.. maybe C++14's alignof?
    char _hack[sizeof(A[3])];
    A* marr;
public:
    B() : marr(reinterpret_cast<A*>(_hack)) {
        new (&marr[0]) A(5);
        new (&marr[1]) A(6);
        new (&marr[2]) A(7);
    }

    A* arr() { return marr; }
};

int main(int argc, char** argv) {
    B b;
    A* arr = b.arr();
    std::cout << arr[0].value() << " " << arr[1].value() << " " << arr[2].value() << "\n";
    return 0;
}

Wenn Sie dies in Ihren Code einfügen, hoffe ich, dass Sie einen sehr guten Grund haben.

0
Moshev

Es gibt keine Array-Konstruktionssyntax, die in diesem Zusammenhang verwendet werden kann, zumindest nicht direkt. Sie können das erreichen, was Sie erreichen möchten, indem Sie folgende Schritte ausführen:

Bar::Bar()
{
    static const int inits [] = {4,5,6};
    static const size_t numInits = sizeof(inits)/sizeof(inits[0]);
    std::copy(&inits[0],&inits[numInits],foo);  // be careful that there are enough slots in foo
}

... aber Sie müssen Foo einen Standardkonstruktor geben.

0
John Dibling

Dies ist meine Lösung für Ihre Referenz:

struct Foo
{
    Foo(){}//used to make compiler happy!
    Foo(int x){/*...*/}
};

struct Bar
{
    Foo foo[3];

    Bar()
    {
        //initialize foo array here:
        for(int i=0;i<3;++i)
        {
            foo[i]=Foo(4+i);
        }
    }
};
0
user7967189

Ideen aus einem verdrehten Kopf:

class mytwistedclass{
static std::vector<int> initVector;
mytwistedclass()
{
    //initialise with initVector[0] and then delete it :-)
}

};

setzen Sie nun dieses initVector auf etwas, das Sie möchten, bevor Sie ein Objekt instanziieren. Dann werden Ihre Objekte mit Ihren Parametern initialisiert.

0
the100rabh