web-dev-qa-db-fra.com

initialiser un tableau const dans un initialiseur de classe en C ++

J'ai la classe suivante en C++:

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

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

La question est de savoir comment initialiser b dans la liste d’initialisation, étant donné que je ne peux pas l’initialiser dans le corps de la fonction du constructeur, car b est const.

Cela ne marche pas:

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

Edit: le cas d'espèce est lorsque je peux avoir différentes valeurs pour b pour différentes instances, mais il est connu que les valeurs sont constantes pour la durée de vie de l'instance.

71
Nathan Fellman

Comme les autres l'ont dit, ISO C++ ne prend pas cela en charge. Mais vous pouvez contourner le problème. Utilisez simplement std :: vector à la place.

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

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

Avec C++ 11, la réponse à cette question a maintenant changé et vous pouvez en fait:

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;
}
72
Flexo

Ce n'est pas possible dans la norme actuelle. Je pense que vous pourrez le faire en C++ 0x en utilisant des listes d’initialisation (voir Aperçu de C++ 0x , de Bjarne Stroustrup, pour plus d’informations sur les listes d’initialisation et d’autres logiciels Nice C. ++ 0x fonctionnalités).

25
Luc Touraille

std::vector utilise le tas. Bon sang, quel gâchis ce ne serait que pour le bien d'un const sanity-check. Le point de std::vector est une croissance dynamique au moment de l'exécution, pas une vérification de la syntaxe ancienne qui devrait être effectuée au moment de la compilation. Si vous ne voulez pas grandir, créez une classe pour envelopper un tableau normal.

#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 et ConstFixedSizeArray sont réutilisables.

La première permet la vérification des limites au moment de l’exécution lors de l’initialisation du tableau (de la même manière qu’un vecteur peut être), qui peut devenir ultérieurement const après cette initialisation.

La seconde permet au tableau d'être alloué à l'intérieur un autre objet, qui pourrait être sur le tas ou simplement la pile si c'est là où se trouve l'objet. Il n'y a pas de perte de temps à allouer du tas. Il effectue également une vérification constante au moment de la compilation sur le tableau.

b_filler est une toute petite classe privée fournissant les valeurs d'initialisation. La taille du tableau est vérifiée au moment de la compilation avec les arguments du modèle, il n'y a donc aucune chance de sortir des limites.

Je suis sûr qu'il existe des moyens plus exotiques de modifier cela. Ceci est un coup de couteau initial. Je pense que vous pouvez compenser les inconvénients du compilateur avec les classes.

12
Matthew

La norme ISO C++ ne vous laisse pas faire cela. Si c'était le cas, la syntaxe serait probablement la suivante:

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

Ou quelque chose du genre. D'après votre question, cela ressemble en fait à ce que vous voulez, c'est un membre de classe constant (ou statique) qui est le tableau. C++ vous laisse faire cela. Ainsi:

#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;
}

La sortie étant:

A::a => 0, 1

Bien sûr, puisqu'il s'agit d'un membre de classe statique, il en est de même pour chaque instance de la classe A. Si ce n'est pas ce que vous voulez, c'est-à-dire que vous voulez que chaque instance de A ait des valeurs d'élément différentes dans le tableau a l'erreur d'essayer de faire le tableau const pour commencer. Vous devriez juste faire ceci:

#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;
}
9
orj

Vous ne pouvez pas faire cela à partir de la liste d'initialisation,

Regardez ça:

http://www.cprogramming.com/tutorial/initialization-lists-c++.html

:)

4
Trap

Là où j'ai un tableau constant, cela a toujours été fait en statique. Si vous pouvez accepter cela, ce code devrait être compilé et exécuté.

#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;
}
4
Daniel Bungert

Une solution sans utiliser le tas avec std::vector est d'utiliser boost::array, bien que vous ne puissiez pas initialiser les membres du tableau directement dans le constructeur.

#include <boost/array.hpp>

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

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

Que diriez-vous d'émuler un tableau const via une fonction d'accesseur? C'est non statique (comme vous l'avez demandé), et cela ne nécessite ni stl ni aucune autre bibliothèque:

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

Du fait que a :: privateB est privé, il est effectivement constant en dehors de a ::, et vous pouvez y accéder de la même manière qu’un tableau, par exemple.

a aobj(2,3);    // initialize "constant array" b[]
n = aobj.b(1);  // read b[1] (write impossible from here)

Si vous souhaitez utiliser une paire de classes, vous pouvez également protéger privateB des fonctions membres. Cela pourrait être fait en héritant d'un; mais je pense que je préfère post comp.lang.c ++ de John Harrison en utilisant une classe const.

3
Pete

fait intéressant, en C #, vous avez le mot-clé const qui se traduit par le type statique de C++, par opposition à readonly, qui ne peut être défini que par les constructeurs et les initialisations, même par des non-constantes, par exemple:

readonly DateTime a = DateTime.Now;

Je conviens que si vous avez un tableau const prédéfini, vous pouvez aussi le rendre statique. À ce stade, vous pouvez utiliser cette syntaxe intéressante:

//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", "!"};

cependant, je n'ai pas trouvé le moyen de contourner la constante '10'. La raison est claire, cependant, il lui faut savoir comment effectuer un accès au tableau. Une alternative possible est d'utiliser #define, mais je n'aime pas cette méthode et #undef à la fin de l'en-tête, avec un commentaire à éditer ici aussi au CPP en cas de changement.

2
Nefzen