Как вернуть массив из функции? - PullRequest
44 голосов
/ 24 ноября 2010

Как я могу вернуть массив из метода и как я должен объявить его?

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

Ответы [ 5 ]

57 голосов
/ 24 ноября 2010

int* test();

но было бы "больше C ++" для использования векторов:

std::vector< int > test();

EDIT
Я уточню кое-что. Поскольку вы упомянули C ++, я буду использовать операторы new[] и delete[], но то же самое с malloc / free.

В первом случае вы напишите что-то вроде:

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

но это не очень хорошая идея, потому что клиент вашей функции на самом деле не знает размер возвращаемого массива, хотя клиент может безопасно освободить его с помощью вызова delete[].

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

Лучшая подпись была бы такой:

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

И ваш код клиента теперь будет:

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.

Поскольку это C ++, `std :: vector является широко используемым решением:

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

Теперь вам не нужно вызывать delete[], так как он будет обрабатываться объектом, и вы можете безопасно повторить его с помощью:

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

, что проще и безопаснее.

17 голосов
/ 24 ноября 2010

как я могу вернуть массив в методе c ++ и как мне его объявить? int [] test (void); ??

Это звучит как простой вопрос, но в C ++ у вас довольно много вариантов. Во-первых, вы должны предпочесть ...

  • std::vector<>, который динамически увеличивается до любого количества элементов, с которыми вы сталкиваетесь во время выполнения, или

  • std::array<> (введено в C ++ 11), в котором всегда хранится количество элементов, указанных во время компиляции,

... поскольку они управляют памятью, обеспечивая правильное поведение и значительно упрощая работу:

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();
}

Практика создания const ссылки на возвращаемые данные иногда позволяет избежать копирования, но обычно вы можете просто положиться на Оптимизацию возвращаемого значения или - для vector, но не array - семантику перемещения (вводится с C ++ 11).

Если вы действительно хотите использовать встроенный массив (в отличие от класса стандартной библиотеки, называемого array, упомянутого выше), один из способов - для вызывающей стороны зарезервировать пространство и указать функции использовать его :

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

Другой вариант - заключить массив в структуру, которая, в отличие от необработанных массивов, является допустимой для возврата по значению из функции:

struct X
{
    int x[10];
};

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

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

Начиная с вышеизложенного, если вы застряли с использованием C ++ 03, вы можете обобщить его в нечто более близкое к 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];
};

Другой вариант - вызвать вызываемую функцию для выделения памяти в куче:

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

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

Чтобы упростить управление объектами кучи, многие программисты на C ++ используют «умные указатели», которые обеспечивают удаление, когда указатель (и) на объект покидают свои области видимости. С С ++ 11:

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

Если вы застряли на C ++ 03, лучше всего посмотреть, доступна ли на вашем компьютере библиотека boost: она обеспечивает boost::shared_array.

Еще один вариант - зарезервировать некоторую статическую память fn(), хотя это НЕ БЕЗОПАСНО ДЛЯ РЕЗЬБЫ и означает, что каждый вызов fn() перезаписывает данные, которые видят все, кто хранит указатели от предыдущих вызовов. Тем не менее, это может быть удобно (и быстро) для простого однопоточного кода.

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...
}
9 голосов
/ 24 ноября 2010

Невозможно вернуть массив из функции C ++. 8.3.5 [dcl.fct] / 6:

Функции не должны иметь тип возвращаемого типа массива или функции [...]

Наиболее часто выбираемые альтернативы - возвращать значение типа класса, где этот класс содержит массив, например,

struct ArrayHolder
{
    int array[10];
};

ArrayHolder test();

Или чтобы вернуть указатель на первый элемент статически или динамически размещенного массива, документация должна указывать пользователю, должен ли он (и если да, то как он должен) освободить массив, на который указывает возвращенный указатель. 1010 *

1012 * Е.Г. *

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

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

Хотя можно вернуть ссылку или указатель на массив, это чрезвычайно редко, так как это более сложный синтаксис без практического преимущества перед любым из вышеперечисленных методов.

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

int (*test5())[10]
{
        static int array[10];
        return &array;
}
2 голосов
/ 24 ноября 2010

Хорошо, если вы хотите вернуть свой массив из функции, вы должны убедиться, что значения не хранятся в стеке, поскольку они исчезнут, когда вы выйдете из функции.или выделите память (или передайте ее, но ваша первоначальная попытка с параметром void).Для вашего метода я бы определил его так:

int *gnabber(){
  static int foo[] = {1,2,3}
  return foo;
}
0 голосов
/ 04 марта 2015

"как я могу вернуть массив в методе c ++ и как я должен объявить его? int [] test (void); ?? "

template <class X>
  class Array
{
  X     *m_data;
  int    m_size;
public:
    // there constructor, destructor, some methods
    int Get(X* &_null_pointer)
    {
        if(!_null_pointer)
        {
            _null_pointer = new X [m_size];
            memcpy(_null_pointer, m_data, m_size * sizeof(X));
            return m_size;
        }
       return 0;
    }
}; 

только для int

class IntArray
{
  int   *m_data;
  int    m_size;
public:
    // there constructor, destructor, some methods
    int Get(int* &_null_pointer)
    {
        if(!_null_pointer)
        {
            _null_pointer = new int [m_size];
            memcpy(_null_pointer, m_data, m_size * sizeof(int));
            return m_size;
        }
       return 0;
    }
}; 

пример

Array<float> array;
float  *n_data = NULL;
int     data_size;
if(data_size = array.Get(n_data))
{     // work with array    }

delete [] n_data;

пример для int

IntArray   array;
int       *n_data = NULL;
int        data_size;
if(data_size = array.Get(n_data))
{  // work with array  }

delete [] n_data;
...