2008-10-02 4 views
65

ich die folgende Klasse in C habe ++:initialisieren ein const-Array in einem Klasseninitialisierer in C++

class a { 
    const int b[2]; 
    // other stuff follows 

    // and here's the constructor 
    a(void); 
} 

Die Frage ist, wie kann ich b in der Initialisierungsliste initialisieren, da ich es nicht initialisieren kann innerhalb des Körpers der Funktion des Konstruktors, weil b const ist?

arbeitet Dies gilt nicht:

a::a(void) : 
    b([2,3]) 
{ 
    // other initialization stuff 
} 

Edit: Der Fall ist, wenn ich unterschiedliche Werte für b für verschiedene Instanzen haben kann, aber die Werte bekannt sind für die gesamte Lebensdauer der Instanz konstant sein .

Antwort

29

Wie die anderen sagten, ISO C++ unterstützt das nicht. Aber du kannst es umgehen. Verwenden Sie stattdessen einfach std :: vector.

int* a = new int[N]; 
// fill a 

class C { 
    const std::vector<int> v; 
public: 
    C():v(a, a+N) {} 
}; 
+8

Das Problem dabei ist, dass es Vektoren verwendet, die zusätzlichen Overhead verursachen. – vy32

+1

Das Problem ist nicht, dass es Vektoren oder eine Art von Speicher im Vergleich zu anderen verwendet. Sie können den Vektor nicht direkt mit einer beliebigen Menge von Werten initialisieren. @ CharlesB techinique arbeitet mit Boost oder Std, um es in zwei Schritten zu tun. –

25

Es ist nicht möglich, in der aktuellen Standard. Ich glaube, Sie können dies in C++ 0x mit Initialisierungslisten tun (siehe A Brief Look at C++0x, von Bjarne Stroustrup, für weitere Informationen über Initialisierungslisten und andere nette C++ 0x-Funktionen).

4

Wo ich ein konstantes Array habe, wurde es immer als statisch gemacht. Wenn Sie das akzeptieren können, sollte dieser Code kompiliert und ausgeführt werden.

#include <stdio.h> 
#include <stdlib.h> 

class a { 
     static const int b[2]; 
public: 
     a(void) { 
       for(int i = 0; i < 2; i++) { 
         printf("b[%d] = [%d]\n", i, b[i]); 
       } 
     } 
}; 

const int a::b[2] = { 4, 2 }; 

int main(int argc, char **argv) 
{ 
     a foo; 
     return 0; 
} 
+1

, die davon ausgeht, dass ich in der Tat ein statisches Element tun wollen, aber das isn Das ist immer der Fall. Ich könnte tatsächlich ein const-Array haben, das unterschiedliche Werte für verschiedene Instanzen der Klasse hat, aber die Werte ändern sich nie während der Lebensdauer der Klasse –

+0

gut, wenn Ihr Konstruktor keine Parameter annimmt, dann hätten alle Instanziierungen sowieso die gleichen Werte. Abgesehen davon hast du recht. – nus

+0

"ISO-Standard C++ lässt Sie nicht" - es ist eine gute Idee zu spezifizieren, welche Version des ISO C++ Standards Sie im Sinn haben – mloskot

8

ISO-Standard C++ lässt Sie dies nicht tun. Wenn dies der Fall wäre, wäre die Syntax wahrscheinlich:

a::a(void) : 
b({2,3}) 
{ 
    // other initialization stuff 
} 

Oder etwas in diese Richtung. Aus Ihrer Frage klingt es tatsächlich wie das, was Sie wollen, ist eine konstante Klasse (aka statisch) Mitglied, das das Array ist. C++ lässt Sie dies tun. Wie so:

#include <iostream> 

class A 
{ 
public: 
    A(); 
    static const int a[2]; 
}; 

const int A::a[2] = {0, 1}; 

A::A() 
{ 
} 

int main (int argc, char * const argv[]) 
{ 
    std::cout << "A::a => " << A::a[0] << ", " << A::a[1] << "\n"; 
    return 0; 
} 

Der Ausgang Wesen:

A::a => 0, 1 

Jetzt natürlich da dies ein statisches Klassenmitglied ist es das gleiche für jede Instanz der Klasse A. Wenn das nicht das, was Sie wollen, Wenn Sie also möchten, dass jede Instanz von A verschiedene Elementwerte im Array hat, dann machen Sie den Fehler, das Array const zu beginnen. Sie sollten nur das tun:

#include <iostream> 

class A 
{ 
public: 
    A(); 
    int a[2]; 
}; 

A::A() 
{ 
    a[0] = 9; // or some calculation 
    a[1] = 10; // or some calculation 
} 

int main (int argc, char * const argv[]) 
{ 
    A v; 
    std::cout << "v.a => " << v.a[0] << ", " << v.a[1] << "\n"; 
    return 0; 
} 
+1

Warum ist es ein Fehler, das Array const zu beginnen? Was, wenn ich möchte, dass die Werte für das Leben der Instanz gleich bleiben, wie eine Art von ID? –

+0

Dann sollten Sie einen Aufzählungstyp verwenden. – orj

+1

Wie würde ich hier einen Enum-Typ verwenden? –

2

interessanterweise in C# haben Sie das Schlüsselwort const, die auf C++ 's static const übersetzt, im Gegensatz zu Nur-Lesen, das nur bei Konstrukteuren und Initialisierungen eingestellt werden kann, auch durch nicht-Konstanten, ex:

readonly DateTime a = DateTime.Now; 

Ich stimme zu, wenn Sie ein const vordefiniertes Array haben, können Sie es auch statisch machen. An diesem Punkt können Sie diese interessante Syntax:

//in header file 
class a{ 
    static const int SIZE; 
    static const char array[][10]; 
}; 
//in cpp file: 
const int a::SIZE = 5; 
const char array[SIZE][10] = {"hello", "cruel","world","goodbye", "!"}; 

aber ich habe nicht einen Weg, um die Konstante ‚10‘ gefunden. Der Grund ist jedoch klar, es braucht es zu wissen, wie man auf das Array zugreifen kann. Eine mögliche Alternative ist die Verwendung von #define, aber ich mag diese Methode nicht und ich #undef am Ende des Headers, mit einem Kommentar, um dort bei CPP auch im Falle einer Änderung zu bearbeiten.

12

std::vector verwendet den Heap. Meine Güte, was für eine Verschwendung wäre das nur für eine const Plausibilitätsprüfung. Der Punkt std::vector ist dynamisches Wachstum zur Laufzeit, nicht irgendeine alte Syntaxprüfung, die zur Kompilierungszeit durchgeführt werden sollte. Wenn Sie nicht wachsen werden, erstellen Sie eine Klasse, die ein normales Array umschließt.

#include <stdio.h> 


template <class Type, size_t MaxLength> 
class ConstFixedSizeArrayFiller { 
private: 
    size_t length; 

public: 
    ConstFixedSizeArrayFiller() : length(0) { 
    } 

    virtual ~ConstFixedSizeArrayFiller() { 
    } 

    virtual void Fill(Type *array) = 0; 

protected: 
    void add_element(Type *array, const Type & element) 
    { 
     if(length >= MaxLength) { 
      // todo: throw more appropriate out-of-bounds exception 
      throw 0; 
     } 
     array[length] = element; 
     length++; 
    } 
}; 


template <class Type, size_t Length> 
class ConstFixedSizeArray { 
private: 
    Type array[Length]; 

public: 
    explicit ConstFixedSizeArray(
     ConstFixedSizeArrayFiller<Type, Length> & filler 
    ) { 
     filler.Fill(array); 
    } 

    const Type *Array() const { 
     return array; 
    } 

    size_t ArrayLength() const { 
     return Length; 
    } 
}; 


class a { 
private: 
    class b_filler : public ConstFixedSizeArrayFiller<int, 2> { 
    public: 
     virtual ~b_filler() { 
     } 

     virtual void Fill(int *array) { 
      add_element(array, 87); 
      add_element(array, 96); 
     } 
    }; 

    const ConstFixedSizeArray<int, 2> b; 

public: 
    a(void) : b(b_filler()) { 
    } 

    void print_items() { 
     size_t i; 
     for(i = 0; i < b.ArrayLength(); i++) 
     { 
      printf("%d\n", b.Array()[i]); 
     } 
    } 
}; 


int main() 
{ 
    a x; 
    x.print_items(); 
    return 0; 
} 

ConstFixedSizeArrayFiller und ConstFixedSizeArray sind wiederverwendbar.

Die erste ermöglicht die Überprüfung der Laufzeitgrenzen während der Initialisierung des Arrays (wie ein Vektor könnte), die später const nach dieser Initialisierung werden kann.

Die zweite ermöglicht die Zuweisung des Arrays innerhalb ein anderes Objekt, das auf dem Heap oder einfach der Stapel sein könnte, wenn das das Objekt ist. Es gibt keine Verschwendung von Zeit, die vom Heap zugewiesen wird. Es führt auch eine Kompilierungszeitprüfung für das Array durch.

b_filler ist eine kleine private Klasse, um die Initialisierungswerte bereitzustellen. Die Größe des Arrays wird zum Zeitpunkt der Kompilierung mit den Vorlagenargumenten überprüft, sodass keine Möglichkeit besteht, sich außerhalb der Grenzen zu bewegen.

Ich bin sicher, es gibt exotischere Möglichkeiten, dies zu ändern. Dies ist ein erster Stich. Ich denke, dass man mit Klassen den Mangel des Compilers ziemlich gut ausgleichen kann.

+3

Was ist wichtig, dass es auf dem Haufen ist? Der Speicher wird während der gesamten Lebensdauer des Objekts verwendet, unabhängig davon, ob es sich auf dem Heap oder dem Stapel befindet. Wenn man bedenkt, dass viele Architekturen den Heap und den Stack auf gegenüberliegenden Seiten desselben Speicherbereichs haben, sodass sie sich theoretisch in der Mitte treffen können, warum ist es dann wichtig, wo sich das Objekt befindet? –

+2

@Nathan Fellman: Dies könnte als eine Überoptimierung angesehen werden, aber in einigen Fällen möchten Sie, dass Ihr Objekt eine Nullzuweisung durchführt (für die Verwendung auf einem Stapel). In diesem Fall ist ein "neues" zu viel und noch mehr, wenn Sie zur Kompilierungszeit wissen, wie viel Sie benötigen. Zum Beispiel weisen einige Implementierungen von std :: vector ihre Elemente in einem internen Puffer zu, anstatt "neu" zu verwenden, wodurch kleine Vektoren ziemlich billig zum Konstruieren/Zerstören gemacht werden. – paercebal

+0

Manchmal optimieren Compiler genug, dass 'std :: vector' und Arrays den exakt gleichen Code ergeben. Meine Güte. –

3

Eine Lösung ohne Verwendung des Heap mit std::vector ist die Verwendung boost::array, obwohl Sie Array-Member nicht direkt im Konstruktor initialisieren können.

#include <boost/array.hpp> 

const boost::array<int, 2> aa={ { 2, 3} }; 

class A { 
    const boost::array<int, 2> b; 
    A():b(aa){}; 
}; 
2

Wie wäre es emulieren eine const-Array über eine Accessor-Funktion? Es ist nicht statisch (wie gewünscht) und es nicht stl oder jede andere Bibliothek erfordern:

class a { 
    int privateB[2]; 
public: 
    a(int b0,b1) { privateB[0]=b0; privateB[1]=b1; } 
    int b(const int idx) { return privateB[idx]; } 
} 

Da ein :: privateB ist privat, es außerhalb eines :: effektiv konstant ist, und Sie können zugreifen es ähnelt einem Array, z

Wenn Sie bereit sind, ein Paar Klassen zu verwenden, können Sie zusätzlich privateB von Mitgliedsfunktionen schützen. Dies könnte durch Vererben eines; aber ich denke, ich ziehe John Harrison's comp.lang.c++ post using a const class.

+0

Dies ist ein interessanter Ansatz! Vielen Dank! –

66

Mit 11 C++ die Antwort auf diese Frage hat sich nun geändert, und Sie können in der Tat:

struct a { 
    const int b[2]; 
    // other bits follow 

    // and here's the constructor 
    a(); 
}; 

a::a() : 
    b{2,3} 
{ 
    // other constructor work 
} 

int main() { 
a a; 
}