web-dev-qa-db-fra.com

Comment retourner un tableau à partir d'une fonction?

Comment puis-je retourner un tableau à partir d'une méthode et comment dois-je le déclarer?

int[] test(void); // ??
44
ewggwegw

int* test();

mais ce serait "plus C++" d'utiliser des vecteurs:

std::vector< int > test();

EDIT
Je vais clarifier un point. Puisque vous avez parlé de C++, je vais aller avec new[] et delete[] _ opérateurs, mais c'est la même chose avec malloc/free.

Dans le premier cas, vous écrirez quelque chose comme:

int* test() {
    return new int[size_needed];
}

mais ce n'est pas une bonne idée car le client de votre fonction ne connaît pas vraiment la taille du tableau que vous retournez, bien que le client puisse le désallouer en toute sécurité avec un appel à delete[].

int* theArray = test();
for (size_t i; i < ???; ++i) { // I don't know what is the array size!
    // ...
}
delete[] theArray; // ok.

Une meilleure signature serait celle-ci:

int* test(size_t& arraySize) {
    array_size = 10;
    return new int[array_size];
}

Et votre code client serait maintenant:

size_t theSize = 0;
int* theArray = test(theSize);
for (size_t i; i < theSize; ++i) { // now I can safely iterate the array
    // ...
}
delete[] theArray; // still ok.

Comme il s’agit de C++, `std :: vector <T> est une solution largement utilisée:

std::vector<int> test() {
    std::vector<int> vector(10);
    return vector;
}

Maintenant, vous n'êtes pas obligé d'appeler delete[], puisqu'il sera manipulé par l'objet et que vous pouvez l'itérer en toute sécurité avec:

std::vector<int> v = test();
std::vector<int>::iterator it = v.begin();
for (; it != v.end(); ++it) {
   // do your things
}

ce qui est plus facile et plus sûr.

57
Simone

comment puis-je retourner un tableau dans une méthode c ++ et comment dois-je le déclarer? int [] test (vide); ??

Cela semble être une question simple, mais en C++, vous avez plusieurs options. Tout d'abord, vous devriez préférer ...

  • std::vector<> , qui s’agrandit de manière dynamique, quel que soit le nombre d’éléments rencontrés à l’exécution, ou

  • std::array<> (introduit avec C++ 11), qui stocke toujours un certain nombre d'éléments spécifiés lors de la compilation,

... car ils gèrent la mémoire pour vous, garantissant un comportement correct et simplifiant considérablement les choses:

std::vector<int> fn()
{
    std::vector<int> x;
    x.Push_back(10);
    return x;
}

std::array<int, 2> fn2()  // C++11
{
    return {3, 4};
}

void caller()
{
    std::vector<int> a = fn();
    const std::vector<int>& b = fn(); // extend lifetime but read-only
                                      // b valid until scope exit/return

    std::array<int, 2> c = fn2();
    const std::array<int, 2>& d = fn2();
}

La pratique de créer une référence const aux données renvoyées peut parfois éviter une copie, mais vous pouvez normalement vous fier à l'optimisation de la valeur de retour ou - pour vector mais pas array - déplacer la sémantique (introduite avec C++ 11).

Si vous voulez vraiment utiliser un tableau incorporé (distinct de la classe de bibliothèque Standard appelée array mentionnée ci-dessus), vous pouvez utiliser l'une des méthodes suivantes: l’appelant pour réserver de l’espace et dire à la fonction de l’utiliser:

void fn(int x[], int n)
{
    for (int i = 0; i < n; ++i)
        x[i] = n;
}

void caller()
{
    // local space on the stack - destroyed when caller() returns
    int x[10];
    fn(x, sizeof x / sizeof x[0]);

    // or, use the heap, lives until delete[](p) called...
    int* p = new int[10];
    fn(p, 10);
}

Une autre option consiste à envelopper le tableau dans une structure qui, contrairement aux tableaux bruts, est légale pour renvoyer en valeur une fonction:

struct X
{
    int x[10];
};

X fn()
{
    X x;
    x.x[0] = 10;
    // ...
    return x;
}

void caller()
{
    X x = fn();
}

À partir de ce qui précède, si vous êtes bloqué avec C++ 03, vous voudrez peut-être le généraliser de manière plus proche du C++ 11 std::array:

template <typename T, size_t N>
struct array
{
    T& operator[](size_t n) { return x[n]; }
    const T& operator[](size_t n) const { return x[n]; }
    size_t size() const { return N; }
    // iterators, constructors etc....
  private:
    T x[N];
};

Une autre option consiste à ce que la fonction appelée alloue de la mémoire sur le tas:

int* fn()
{
    int* p = new int[2];
    p[0] = 0;
    p[1] = 1;
    return p;
}

void caller()
{
    int* p = fn();
    // use p...
    delete[] p;
}

Pour simplifier la gestion des objets de tas, de nombreux programmeurs C++ utilisent des "pointeurs intelligents" qui garantissent la suppression lorsque le ou les pointeurs de l'objet quittent leur étendue. Avec C++ 11:

std::shared_ptr<int> p(new int[2], [](int* p) { delete[] p; } );
std::unique_ptr<int[]> p(new int[3]);

Si vous êtes bloqué sur C++ 03, la meilleure option est de voir si la bibliothèque boost est disponible sur votre ordinateur: elle fournit boost::shared_array.

Une autre option consiste à réserver de la mémoire statique à fn(), bien que cela ne soit PAS THREAD SAFE et que chaque appel à fn() écrase les données consultées par quiconque conserve les pointeurs des appels précédents. Cela dit, cela peut être pratique (et rapide) pour du code simple à un seul thread.

int* fn(int n)
{
    static int x[2];  // clobbered by each call to fn()
    x[0] = n;
    x[1] = n + 1;
    return x;  // every call to fn() returns a pointer to the same static x memory
}

void caller()
{
    int* p = fn(3);
    // use p, hoping no other thread calls fn() meanwhile and clobbers the values...
    // no clean up necessary...
}
17
Tony Delroy

Il n'est pas possible de renvoyer un tableau à partir d'une fonction C++. 8.3.5 [dcl.fct]/6:

Les fonctions ne doivent pas avoir de type de retour de type tableau ou fonction [...]

Le plus souvent, les alternatives choisies sont de renvoyer une valeur de type classe où cette classe contient un tableau, par exemple.

struct ArrayHolder
{
    int array[10];
};

ArrayHolder test();

Ou pour renvoyer un pointeur sur le premier élément d'un tableau alloué statiquement ou dynamiquement, la documentation doit indiquer à l'utilisateur s'il doit (et le cas échéant, comment il devrait) libérer le tableau auquel le pointeur renvoyé se réfère.

Par exemple.

int* test2()
{
    return new int[10];
}

int* test3()
{
    static int array[10];
    return array;
}

Bien qu'il soit possible de renvoyer une référence ou un pointeur à un tableau, il est extrêmement rare car il s'agit d'une syntaxe plus complexe sans aucun avantage pratique par rapport aux méthodes ci-dessus.

int (&test4())[10]
{
        static int array[10];
        return array;
}

int (*test5())[10]
{
        static int array[10];
        return &array;
}
9
CB Bailey

Eh bien, si vous voulez renvoyer votre tableau à partir d’une fonction, vous devez vous assurer que les valeurs ne sont pas stockées dans la pile, car elles disparaîtront lorsque vous quitterez la fonction.

Vous pouvez donc rendre votre tableau statique ou allouer la mémoire (ou le transmettre, mais votre tentative initiale est avec un paramètre void). Pour votre méthode, je le définirais comme ceci:

int *gnabber(){
  static int foo[] = {1,2,3}
  return foo;
}
2
Mark