【问题标题】:How to return an array from a function?如何从函数返回数组?
【发布时间】:2011-05-14 22:32:38
【问题描述】:

如何从方法中返回一个数组,我必须如何声明它?

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

【问题讨论】:

  • 此问题已作为标题非常相似的问题的副本而关闭。然而,这个问题是关于返回一个数组作为参数传递给函数。这是一个非常不同的命题,意味着这是一个不同的(而且更有趣的问题)。
  • 您返回 std::arraystd::vector不是 C 风格的数组。

标签: c++ arrays method-signature


【解决方案1】:

int* test();

但使用向量会是“更多的 C++”:

std::vector< int > test();

编辑
我会澄清一点。既然你提到了 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&lt;T&gt; 是一个广泛使用的解决方案:

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
}

这样更简单、更安全。

【讨论】:

  • 我会返回std::vector
  • 就我个人而言,我认为回复int* test(); 来回答“如何在 C++ 方法中返回数组?”[原文如此] 如果您不解释它实际上并没有返回,那么会产生误导一个数组但一个指针。
  • 我添加了一些必要的说明。
  • 将向量的引用传递给函数,然后函数根据需要添加任意数量的元素,这不是更有效吗?然后,您不必复制整个向量,包括所有元素(当您返回向量时会发生这种情况,对吗?)
  • @Kapichu 如果 RVO 失败,向量将被移动,而不是复制。
【解决方案2】:

如何在 C++ 方法中返回一个数组,我必须如何声明它? int[] 测试(无效); ??

这听起来像一个简单的问题,但在 C++ 中你有很多选择。首先,你应该更喜欢...

  • std::vector&lt;&gt;,无论您在运行时遇到多少元素,它都会动态增长,或者

  • std::array&lt;&gt;(随 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)。

如果你真的想使用 inbuilt 数组(不同于上面提到的标准库类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++ 程序员使用“智能指针”来确保在指向对象的指针离开其作用域时删除。使用 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...
}

【讨论】:

  • 我不相信new[] int(10) 是一个有效的新表达式。你是说new int[10] 吗?
  • @Charles:可能,我讨厌整天被困在 C# 中,然后试图在这里回答问题……啊…… ;-) 完全听进去了。
【解决方案3】:

"我如何在 C++ 方法中返回一个数组,我必须如何声明它? int[] 测试(无效); ??”

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

只为整数

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;

【讨论】:

    【解决方案4】:

    不能从 C++ 函数返回数组。 8.3.5[dcl.fct]/6:

    函数的返回类型不得为数组或函数[...]

    最常用的替代方法是返回一个类类型的值,其中该类包含一个数组,例如

    struct ArrayHolder
    {
        int array[10];
    };
    
    ArrayHolder test();
    

    或者要返回指向静态或动态分配数组的第一个元素的指针,文档必须向用户说明他是否需要(如果需要,他应该如何)释放返回的指针指向的数组。

    例如

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

    【讨论】:

      【解决方案5】:

      如果你想从一个函数返回你的数组,你必须确保这些值没有存储在堆栈中,因为当你离开函数时它们会消失。

      所以要么使您的数组静态或分配内存(或将其传入,但您最初的尝试是使用 void 参数)。对于你的方法,我会这样定义:

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

      【讨论】:

        猜你喜欢
        • 2022-10-01
        • 2012-06-17
        • 2011-08-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多